From 66700035cca146103642929e7ed0ec2f945e30f6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 6 Nov 2025 14:31:59 +0800 Subject: [PATCH 001/438] Bump project version to 0.1.8-SNAPSHOT Updated the property in pom.xml to prepare for the next development iteration. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2d4bac37e..3f1676d72 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ - 0.1.7-SNAPSHOT + 0.1.8-SNAPSHOT From a5637bb83b31a06ff8ad395d1bff748caddd65d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 01:12:48 +0000 Subject: [PATCH 002/438] Bump ch.qos.logback:logback-classic from 1.5.20 to 1.5.22 Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.20 to 1.5.22. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.5.20...v_1.5.22) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-version: 1.5.22 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 15ce8fdc7..c742e0ba6 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -24,7 +24,7 @@ 1.3.2 6.2.12 2.0.17 - 1.5.20 + 1.5.22 6.0.1 1.37 From f03f987d0c21e7da0aae6e587960e4495b7c6063 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 01:12:57 +0000 Subject: [PATCH 003/438] Bump org.springframework:spring-framework-bom from 6.2.12 to 7.0.2 Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.12 to 7.0.2. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.12...v7.0.2) --- updated-dependencies: - dependency-name: org.springframework:spring-framework-bom dependency-version: 7.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 15ce8fdc7..145739b1a 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -22,7 +22,7 @@ 1.3.2 3.0.2 1.3.2 - 6.2.12 + 7.0.2 2.0.17 1.5.20 From 397aaf026fe97e81740e87420d7bdb78cce522d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 01:13:48 +0000 Subject: [PATCH 004/438] Bump org.springframework:spring-framework-bom from 6.2.12 to 7.0.2 Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.12 to 7.0.2. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.12...v7.0.2) --- updated-dependencies: - dependency-name: org.springframework:spring-framework-bom dependency-version: 7.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 15ce8fdc7..145739b1a 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -22,7 +22,7 @@ 1.3.2 3.0.2 1.3.2 - 6.2.12 + 7.0.2 2.0.17 1.5.20 From 4d3aa15abb29763d092b4411604365b9148a9301 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 01:17:13 +0000 Subject: [PATCH 005/438] Bump ch.qos.logback:logback-classic from 1.5.20 to 1.5.24 Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.20 to 1.5.24. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.5.20...v_1.5.24) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-version: 1.5.24 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 15ce8fdc7..7a6cb6f4c 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -24,7 +24,7 @@ 1.3.2 6.2.12 2.0.17 - 1.5.20 + 1.5.24 6.0.1 1.37 From 3e1afef1e8cfcdc11466031bf1c4e895bdd20cda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 01:17:21 +0000 Subject: [PATCH 006/438] Bump junit.version from 6.0.1 to 6.0.2 Bumps `junit.version` from 6.0.1 to 6.0.2. Updates `org.junit.jupiter:junit-jupiter` from 6.0.1 to 6.0.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r6.0.1...r6.0.2) Updates `org.junit.jupiter:junit-jupiter-engine` from 6.0.1 to 6.0.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r6.0.1...r6.0.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 15ce8fdc7..3894364cb 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -26,7 +26,7 @@ 2.0.17 1.5.20 - 6.0.1 + 6.0.2 1.37 From cdf3f10c2ec1ff80098131337c14ebe64b2b4302 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 01:33:56 +0000 Subject: [PATCH 007/438] Bump org.springframework:spring-framework-bom from 7.0.2 to 7.0.3 Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 7.0.2 to 7.0.3. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v7.0.2...v7.0.3) --- updated-dependencies: - dependency-name: org.springframework:spring-framework-bom dependency-version: 7.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 6172f5df4..23460ca6c 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -22,7 +22,7 @@ 1.3.2 3.0.2 1.3.2 - 7.0.2 + 7.0.3 2.0.17 1.5.24 From 644410adde3e5b4f1bb3e23430ce960bca4b8038 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 01:35:43 +0000 Subject: [PATCH 008/438] Bump ch.qos.logback:logback-classic from 1.5.24 to 1.5.26 Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.24 to 1.5.26. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.5.24...v_1.5.26) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-version: 1.5.26 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 6172f5df4..1df0d1f80 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -24,7 +24,7 @@ 1.3.2 7.0.2 2.0.17 - 1.5.24 + 1.5.26 6.0.2 1.37 From 824e2f0781630eed087c43cd58e2c67cd54f2afe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 01:46:57 +0000 Subject: [PATCH 009/438] Bump org.springframework:spring-framework-bom from 7.0.2 to 7.0.3 Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 7.0.2 to 7.0.3. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v7.0.2...v7.0.3) --- updated-dependencies: - dependency-name: org.springframework:spring-framework-bom dependency-version: 7.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 6172f5df4..23460ca6c 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -22,7 +22,7 @@ 1.3.2 3.0.2 1.3.2 - 7.0.2 + 7.0.3 2.0.17 1.5.24 From 701a44d520d6105e1cf4dbf65d7f143ed0424c57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 01:47:02 +0000 Subject: [PATCH 010/438] Bump ch.qos.logback:logback-classic from 1.5.24 to 1.5.27 Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.24 to 1.5.27. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.5.24...v_1.5.27) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-version: 1.5.27 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 6172f5df4..139d38ede 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -24,7 +24,7 @@ 1.3.2 7.0.2 2.0.17 - 1.5.24 + 1.5.27 6.0.2 1.37 From 4099b4acc6a40db82de5e33c51687ef984cca2a8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Feb 2026 21:41:03 +0800 Subject: [PATCH 011/438] Add microsphere-jdk-tools module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new microsphere-jdk-tools Maven module. Adds pom.xml with dependencies (microsphere-java-core, JUnit, Mockito, Logback), maven-compiler config and maven-shade setup. Implements io.microsphere.jdk.tools.compiler.Compiler — a utility to programmatically compile Java sources, manage source/target paths, detect class/root/source locations, and configure processors/options/diagnostics. Includes unit tests (CompilerTest) covering constructors, default path detection, options, processors and compile flow, and a logback-test.xml for test logging. --- microsphere-jdk-tools/pom.xml | 90 ++++++ .../jdk/tools/compiler/Compiler.java | 279 ++++++++++++++++++ .../jdk/tools/compiler/CompilerTest.java | 188 ++++++++++++ .../src/test/resources/logback-test.xml | 15 + 4 files changed, 572 insertions(+) create mode 100644 microsphere-jdk-tools/pom.xml create mode 100644 microsphere-jdk-tools/src/main/java/io/microsphere/jdk/tools/compiler/Compiler.java create mode 100644 microsphere-jdk-tools/src/test/java/io/microsphere/jdk/tools/compiler/CompilerTest.java create mode 100644 microsphere-jdk-tools/src/test/resources/logback-test.xml diff --git a/microsphere-jdk-tools/pom.xml b/microsphere-jdk-tools/pom.xml new file mode 100644 index 000000000..d16b92c62 --- /dev/null +++ b/microsphere-jdk-tools/pom.xml @@ -0,0 +1,90 @@ + + + + io.github.microsphere-projects + microsphere-java-parent + ${revision} + ../microsphere-java-parent/pom.xml + + 4.0.0 + + io.github.microsphere-projects + microsphere-jdk-tools + ${revision} + jar + + Microsphere :: JDK :: Tools + Microsphere JDK Tools + + + + + + io.github.microsphere-projects + microsphere-java-core + ${revision} + + + + + org.junit.jupiter + junit-jupiter + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + org.mockito + mockito-core + test + + + + + ch.qos.logback + logback-classic + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + -proc:none + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + io.github.microsphere-projects:microsphere-java-core + + + + + + + + + + \ No newline at end of file diff --git a/microsphere-jdk-tools/src/main/java/io/microsphere/jdk/tools/compiler/Compiler.java b/microsphere-jdk-tools/src/main/java/io/microsphere/jdk/tools/compiler/Compiler.java new file mode 100644 index 000000000..09ab04336 --- /dev/null +++ b/microsphere-jdk-tools/src/main/java/io/microsphere/jdk/tools/compiler/Compiler.java @@ -0,0 +1,279 @@ +/* + * 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. + */ +package io.microsphere.jdk.tools.compiler; + +import io.microsphere.logging.Logger; + +import javax.annotation.processing.Processor; +import javax.tools.DiagnosticListener; +import javax.tools.JavaCompiler; +import javax.tools.JavaCompiler.CompilationTask; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.Charset; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Set; + +import static io.microsphere.collection.CollectionUtils.addAll; +import static io.microsphere.collection.CollectionUtils.first; +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.collection.SetUtils.newLinkedHashSet; +import static io.microsphere.collection.Sets.ofSet; +import static io.microsphere.constants.FileConstants.JAVA_EXTENSION; +import static io.microsphere.constants.SymbolConstants.DOT_CHAR; +import static io.microsphere.io.scanner.SimpleFileScanner.INSTANCE; +import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.text.FormatUtils.format; +import static io.microsphere.util.ArrayUtils.ofArray; +import static io.microsphere.util.ClassUtils.getTypeName; +import static io.microsphere.util.StringUtils.substringBefore; +import static java.io.File.separatorChar; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableSet; +import static java.util.stream.Stream.of; +import static javax.tools.StandardLocation.CLASS_OUTPUT; +import static javax.tools.StandardLocation.SOURCE_OUTPUT; +import static javax.tools.StandardLocation.SOURCE_PATH; +import static javax.tools.ToolProvider.getSystemJavaCompiler; + +/** + * The Java Compiler + * + * @author Mercy + * @since 1.0.0 + */ +public class Compiler { + + private static final Logger logger = getLogger(Compiler.class); + + public static final String[] DEFAULT_OPTIONS = ofArray("-parameters", "-Xlint:unchecked", "-nowarn", "-Xlint:deprecation"); + + private final Set sourcePaths; + + private final File targetDirectory; + + private final JavaCompiler javaCompiler; + + private Set processors; + + private List options; + + private DiagnosticListener diagnosticListener; + + private Locale locale; + + private Charset charset; + + public Compiler() { + this(defaultTargetDirectory()); + } + + public Compiler(File targetDirectory) { + this(defaultSourceDirectory(), targetDirectory); + } + + public Compiler(File defaultSourceDirectory, File targetDirectory) { + options(DEFAULT_OPTIONS); + this.sourcePaths = newLinkedHashSet(defaultSourceDirectory); + this.targetDirectory = targetDirectory; + this.javaCompiler = getSystemJavaCompiler(); + } + + public Compiler options(String... options) { + this.options = ofList(options); + return this; + } + + public Compiler sourcePaths(File... sourcePaths) { + addAll(this.sourcePaths, sourcePaths); + return this; + } + + public Compiler sourcePaths(Class... sourceClasses) { + for (Class sourceClass : sourceClasses) { + sourcePath(sourceClass); + } + return this; + } + + public Compiler sourcePath(Class sourceClass) { + File sourcePath = detectSourcePath(sourceClass); + if (sourcePath != null) { + return sourcePaths(sourcePath); + } + return this; + } + + public Compiler processors(Processor... processors) { + this.processors = ofSet(processors); + return this; + } + + public Compiler diagnosticListener(DiagnosticListener diagnosticListener) { + this.diagnosticListener = diagnosticListener; + return this; + } + + public Compiler locale(Locale locale) { + this.locale = locale; + return this; + } + + public Compiler charset(Charset charset) { + this.charset = charset; + return this; + } + + public boolean compile(Class... sourceClasses) throws IOException { + JavaCompiler javaCompiler = getJavaCompiler(); + StandardJavaFileManager javaFileManager = getJavaFileManager(); + CompilationTask task = javaCompiler.getTask(null, javaFileManager, + getDiagnosticListener(), getOptions(), null, getJavaFileObjects(javaFileManager, sourceClasses)); + task.setProcessors(this.getProcessors()); + return task.call(); + } + + public JavaCompiler getJavaCompiler() { + return this.javaCompiler; + } + + public StandardJavaFileManager getJavaFileManager() throws IOException { + StandardJavaFileManager javaFileManager = getJavaCompiler().getStandardFileManager(getDiagnosticListener(), getLocale(), getCharset()); + javaFileManager.setLocation(SOURCE_PATH, this.sourcePaths); + javaFileManager.setLocation(CLASS_OUTPUT, singleton(this.targetDirectory)); + javaFileManager.setLocation(SOURCE_OUTPUT, singleton(this.targetDirectory)); + return javaFileManager; + } + + public DiagnosticListener getDiagnosticListener() { + return this.diagnosticListener; + } + + public Locale getLocale() { + return this.locale; + } + + public Charset getCharset() { + return this.charset; + } + + public List getOptions() { + List options = this.options; + if (isEmpty(options)) { + return emptyList(); + } + return unmodifiableList(options); + } + + public Set getProcessors() { + Set processors = this.processors; + if (processors == null) { + return emptySet(); + } + return unmodifiableSet(processors); + } + + private Iterable getJavaFileObjects(StandardJavaFileManager javaFileManager, Class... sourceClasses) { + File[] javaFiles = of(sourceClasses) + .map(this::searchJavaSourceFile) + .filter(Objects::nonNull) + .toArray(File[]::new); + return javaFileManager.getJavaFileObjects(javaFiles); + } + + private File searchJavaSourceFile(Class sourceClass) { + String javaSourceFilePath = resolveJavaSourceFileRelativePath(sourceClass); + for (File sourceDirectory : this.sourcePaths) { + File javaSourceFile = new File(sourceDirectory, javaSourceFilePath); + if (javaSourceFile.exists()) { + return javaSourceFile; + } + } + return null; + } + + static File defaultSourceDirectory() { + return detectSourcePath(Compiler.class); + } + + static File defaultRootDirectory() { + return detectRootDirectory(Compiler.class); + } + + static File defaultTargetDirectory() { + File dir = new File(defaultRootDirectory(), "target/generated-classes"); + dir.mkdirs(); + return dir; + } + + public static File detectSourcePath(Class sourceClass) { + File rootDirectory = detectRootDirectory(sourceClass); + String javaSourceFileRelativePath = resolveJavaSourceFileRelativePath(sourceClass); + + Set sourceFiles = INSTANCE.scan(rootDirectory, true, + file -> file.getAbsolutePath().endsWith(javaSourceFileRelativePath)); + + File sourceFile = first(sourceFiles); + if (sourceFile == null) { + logger.trace("The source files of {} can't be found in the root directory[path :'{}']", sourceClass, rootDirectory); + return null; + } + + String javaSourceFilePath = sourceFile.getAbsolutePath(); + String javaSourcePath = substringBefore(javaSourceFilePath, javaSourceFileRelativePath); + File sourcePath = new File(javaSourcePath); + + logger.trace("The source file[path : '{}] of {} was found in the source directory[path :'{}']", sourceFile, sourceClass, sourcePath); + + return sourcePath; + } + + public static File detectRootDirectory(Class sourceClass) { + File classPath = detectClassPath(sourceClass); + // classPath : "${rootDirectory}/target/classes" + File rootDirectory = classPath.getParentFile().getParentFile(); + logger.trace("The root directory[path : '{}'] was found by the source class[name : '{}']", + rootDirectory.getAbsolutePath(), getTypeName(sourceClass)); + return rootDirectory; + } + + public static File detectClassPath(Class sourceClass) { + ProtectionDomain protectionDomain = sourceClass.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + if (codeSource != null) { + URL location = codeSource.getLocation(); + return new File(location.getPath()); + } + String message = format("The source {} is based on the file system, the class path can't be detected.", sourceClass); + throw new UnsupportedOperationException(message); + } + + public static String resolveJavaSourceFileRelativePath(Class sourceClass) { + return sourceClass.getName().replace(DOT_CHAR, separatorChar).concat(JAVA_EXTENSION); + } +} \ No newline at end of file diff --git a/microsphere-jdk-tools/src/test/java/io/microsphere/jdk/tools/compiler/CompilerTest.java b/microsphere-jdk-tools/src/test/java/io/microsphere/jdk/tools/compiler/CompilerTest.java new file mode 100644 index 000000000..78023fafc --- /dev/null +++ b/microsphere-jdk-tools/src/test/java/io/microsphere/jdk/tools/compiler/CompilerTest.java @@ -0,0 +1,188 @@ +package io.microsphere.jdk.tools.compiler; + +import org.junit.jupiter.api.Test; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.TypeElement; +import java.io.File; +import java.io.IOException; +import java.util.Set; + +import static io.microsphere.jdk.tools.compiler.Compiler.defaultRootDirectory; +import static io.microsphere.jdk.tools.compiler.Compiler.defaultSourceDirectory; +import static io.microsphere.jdk.tools.compiler.Compiler.defaultTargetDirectory; +import static io.microsphere.jdk.tools.compiler.Compiler.detectClassPath; +import static io.microsphere.jdk.tools.compiler.Compiler.detectRootDirectory; +import static io.microsphere.jdk.tools.compiler.Compiler.detectSourcePath; +import static io.microsphere.jdk.tools.compiler.Compiler.resolveJavaSourceFileRelativePath; +import static java.io.File.separatorChar; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Locale.getDefault; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the Compiler class + * + * @see Compiler + */ +class CompilerTest { + + @Test + void testDefaultConstructor() { + // Test default constructor which uses default target directory + Compiler compiler = new Compiler(); + + assertNotNull(compiler, "Compiler should be instantiated successfully"); + assertNotNull(compiler.getJavaCompiler(), "JavaCompiler should be initialized"); + } + + @Test + void testTargetDirectoryConstructor() { + File targetDir = new File("target/test-classes"); + targetDir.mkdirs(); // Ensure directory exists + + Compiler compiler = new Compiler(targetDir); + + assertNotNull(compiler, "Compiler should be instantiated successfully with target directory"); + assertNotNull(compiler.getJavaCompiler(), "JavaCompiler should be initialized"); + } + + @Test + void testSourceAndTargetDirectoryConstructor() { + File sourceDir = new File("src/test/java"); + File targetDir = new File("target/test-classes"); + targetDir.mkdirs(); // Ensure target directory exists + + Compiler compiler = new Compiler(sourceDir, targetDir); + + assertNotNull(compiler, "Compiler should be instantiated successfully with source and target directories"); + assertNotNull(compiler.getJavaCompiler(), "JavaCompiler should be initialized"); + } + + @Test + void testDefaultDirectories() { + // Test the static methods that determine default directories + File defaultSourceDir = defaultSourceDirectory(); + File defaultTargetDir = defaultTargetDirectory(); + File defaultRootDir = defaultRootDirectory(); + + // Root directory should exist (it's derived from the Compiler class location) + assertNotNull(defaultSourceDir, "Default source directory should not be null"); + assertNotNull(defaultRootDir, "Default root directory should not be null"); + assertTrue(defaultRootDir.exists(), "Default root directory should exist"); + + // Target directory should be created + assertNotNull(defaultTargetDir, "Default target directory should not be null"); + } + + @Test + void testDetectClassPath() { + File classPath = detectClassPath(Compiler.class); + + assertNotNull(classPath, "Detected class path should not be null"); + assertTrue(classPath.exists(), "Detected class path should exist"); + + assertThrows(UnsupportedOperationException.class, () -> detectClassPath(String.class)); + } + + @Test + void testResolveJavaSourceFileRelativePath() { + String expectedPath = Compiler.class.getName() + .replace('.', separatorChar) + .concat(".java"); + String actualPath = resolveJavaSourceFileRelativePath(Compiler.class); + + assertEquals(expectedPath, actualPath, + "Resolved Java source file relative path should match expected format"); + } + + @Test + void testSourcePathsMethod() { + File targetDir = new File("target/test-classes"); + targetDir.mkdirs(); + + Compiler compiler = new Compiler(targetDir); + + File testSourcePath = new File("src/main/java"); + Compiler result = compiler.sourcePaths(testSourcePath); + + // Verify method chaining returns the same instance + assertSame(compiler, result, "sourcePaths method should return the same instance for chaining"); + + result = compiler.sourcePaths(Compiler.class, Test.class); + assertSame(compiler, result, "sourcePaths method should return the same instance for chaining"); + } + + @Test + void testProcessorsMethod() { + File targetDir = new File("target/test-classes"); + targetDir.mkdirs(); + + Compiler compiler = new Compiler(targetDir); + Processor processor = new AbstractProcessor() { + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } + }; + + Compiler result = compiler.processors(processor); + + // Verify method chaining returns the same instance + assertSame(compiler, result, "processors method should return the same instance for chaining"); + } + + @Test + void testCompile() throws IOException { + Compiler compiler = new Compiler(); + + // This would normally attempt compilation, but with mocked compiler it's safe to call + boolean result = compiler.compile(Compiler.class, Test.class); + + // For this test, we're primarily verifying the setup doesn't fail + // Actual compilation success depends on the mock setup + assertTrue(result, "Test setup completed without exceptions"); + + compiler.options() + .processors(new AbstractProcessor() { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } + }) + .diagnosticListener(diagnostic -> { + }) + .locale(getDefault()) + .charset(UTF_8) + ; + + result = compiler.compile(Compiler.class); + + assertTrue(result, "Test setup completed without exceptions"); + } + + @Test + void testDetectRootDirectory() { + File rootDir = detectRootDirectory(Compiler.class); + + assertNotNull(rootDir, "Root directory should not be null"); + assertTrue(rootDir.exists(), "Root directory should exist"); + } + + @Test + void testDetectSourcePath() { + // This might be null if source path cannot be detected in test environment + // but the method should not throw exceptions + assertNotNull(detectSourcePath(Compiler.class), "Should be able to call detectSourcePath without errors"); + assertNotNull(detectSourcePath(CompilerTest.class), "Should be able to call detectSourcePath without errors"); + assertNull(detectSourcePath(Test.class), "Should be able to call detectSourcePath without errors"); + } +} diff --git a/microsphere-jdk-tools/src/test/resources/logback-test.xml b/microsphere-jdk-tools/src/test/resources/logback-test.xml new file mode 100644 index 000000000..e3375b74c --- /dev/null +++ b/microsphere-jdk-tools/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + + ${ENCODER_PATTERN} + + + + + + + + \ No newline at end of file From 3e9fae47bde58535c253b39492c562c34b3dd029 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Feb 2026 22:26:30 +0800 Subject: [PATCH 012/438] Add Mockito and bump test dependency versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce Mockito to the parent POM: add a mockito.version property and a mockito-core dependency so tests can use Mockito. Also update JUnit versions: bump the root junit.version 6.0.1 → 6.0.2 and the profile's junit.version 5.13.4 → 5.14.2. The profile also sets mockito.version (4.11.0) to override the parent value. --- microsphere-java-parent/pom.xml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 25ab0a0db..4d1d66ae9 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -26,7 +26,8 @@ 2.0.17 1.5.22 - 6.0.1 + 6.0.2 + 5.14.2 1.37 @@ -82,6 +83,14 @@ ${junit.version} + + + org.mockito + mockito-core + ${mockito.version} + + + org.openjdk.jmh jmh-core @@ -126,7 +135,8 @@ 5.3.39 - 5.13.4 + 5.14.2 + 4.11.0 From e6d5a4a9699957deb0be30b0b21c5f766a1d9f50 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Feb 2026 22:27:22 +0800 Subject: [PATCH 013/438] Add microsphere-java-test module Introduce a new microsphere-java-test Maven module providing testing utilities and fixtures. Adds pom.xml (compiler/shade configuration and test dependencies), a TestAnnotation and annotation-processing test infrastructure (AbstractAnnotationProcessingTest, AnnotationProcessingTestProcessor, CompilerInvocationInterceptor) that compiles test sources and runs processors during JUnit5 tests. Also adds a set of model classes, service interfaces/implementations, unit tests, logback-test.xml, and a service provider entry for annotation processors to support annotation-processing test cases. --- microsphere-java-test/pom.xml | 133 +++++++++++++ .../test/annotation/TestAnnotation.java | 73 +++++++ .../AbstractAnnotationProcessingTest.java | 153 +++++++++++++++ .../AnnotationProcessingTestProcessor.java | 87 +++++++++ .../CompilerInvocationInterceptor.java | 59 ++++++ .../io/microsphere/test/model/Ancestor.java | 38 ++++ .../test/model/ArrayTypeModel.java | 76 ++++++++ .../test/model/CollectionTypeModel.java | 82 ++++++++ .../java/io/microsphere/test/model/Color.java | 47 +++++ .../model/ConfigurationPropertyModel.java | 85 ++++++++ .../microsphere/test/model/MapTypeModel.java | 82 ++++++++ .../java/io/microsphere/test/model/Model.java | 90 +++++++++ .../io/microsphere/test/model/Parent.java | 66 +++++++ .../test/model/PrimitiveTypeModel.java | 74 +++++++ .../test/model/SimpleTypeModel.java | 162 ++++++++++++++++ .../test/model/StringArrayList.java | 30 +++ .../test/service/DefaultTestService.java | 58 ++++++ .../test/service/GenericTestService.java | 33 ++++ .../microsphere/test/service/TestService.java | 56 ++++++ .../test/service/TestServiceImpl.java | 101 ++++++++++ .../src/main/resources/logback-test.xml | 15 ++ .../processing/AnnotationProcessingTest.java | 71 +++++++ .../annotation/processing/TestProcessor.java | 48 +++++ .../microsphere/test/model/AncestorTest.java | 81 ++++++++ .../test/model/ArrayTypeModelTest.java | 122 ++++++++++++ .../test/model/CollectionTypeModelTest.java | 138 +++++++++++++ .../io/microsphere/test/model/ColorTest.java | 103 ++++++++++ .../model/ConfigurationPropertyModelTest.java | 152 +++++++++++++++ .../test/model/MapTypeModelTest.java | 138 +++++++++++++ .../io/microsphere/test/model/ModelTest.java | 116 +++++++++++ .../io/microsphere/test/model/ParentTest.java | 86 +++++++++ .../test/model/PrimitiveTypeModelTest.java | 81 ++++++++ .../test/model/SimpleTypeModelTest.java | 182 ++++++++++++++++++ .../test/model/StringArrayListTest.java | 137 +++++++++++++ .../test/service/DefaultTestServiceTest.java | 83 ++++++++ .../test/service/GenericTestServiceTest.java | 58 ++++++ .../javax.annotation.processing.Processor | 1 + 37 files changed, 3197 insertions(+) create mode 100644 microsphere-java-test/pom.xml create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/annotation/TestAnnotation.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/Ancestor.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/ArrayTypeModel.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/CollectionTypeModel.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/Color.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/ConfigurationPropertyModel.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/MapTypeModel.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/Model.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/Parent.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/PrimitiveTypeModel.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/SimpleTypeModel.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/model/StringArrayList.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/service/DefaultTestService.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/service/GenericTestService.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/service/TestService.java create mode 100644 microsphere-java-test/src/main/java/io/microsphere/test/service/TestServiceImpl.java create mode 100644 microsphere-java-test/src/main/resources/logback-test.xml create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/TestProcessor.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/AncestorTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/ArrayTypeModelTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/CollectionTypeModelTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/ColorTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/ConfigurationPropertyModelTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/MapTypeModelTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/ModelTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/ParentTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/PrimitiveTypeModelTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/SimpleTypeModelTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/model/StringArrayListTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/service/DefaultTestServiceTest.java create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/service/GenericTestServiceTest.java create mode 100644 microsphere-java-test/src/test/resources/META-INF/services/javax.annotation.processing.Processor diff --git a/microsphere-java-test/pom.xml b/microsphere-java-test/pom.xml new file mode 100644 index 000000000..9d94d023b --- /dev/null +++ b/microsphere-java-test/pom.xml @@ -0,0 +1,133 @@ + + + + io.github.microsphere-projects + microsphere-java-parent + ${revision} + ../microsphere-java-parent/pom.xml + + 4.0.0 + + io.github.microsphere-projects + microsphere-java-test + ${revision} + jar + + Microsphere :: Java :: Test + Microsphere Java Test + + + 2.1 + 2.3.1 + + + + + + + io.github.microsphere-projects + microsphere-java-core + ${revision} + true + + + + + io.github.microsphere-projects + microsphere-jdk-tools + ${revision} + true + + + + + org.junit.jupiter + junit-jupiter + true + + + + + org.mockito + mockito-core + true + + + + org.junit.jupiter + junit-jupiter-engine + true + + + + + ch.qos.logback + logback-classic + true + + + + + javax.ws.rs + javax.ws.rs-api + ${javax.ws.rs.version} + true + + + + + javax.xml.ws + jaxws-api + ${jaxws-api.version} + true + + + + + org.springframework + spring-context + true + + + + org.springframework + spring-web + true + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + -proc:none + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + io.github.microsphere-projects:microsphere-java-core + + + + + + + + + + \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/TestAnnotation.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/TestAnnotation.java new file mode 100644 index 000000000..5669e4936 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/TestAnnotation.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package io.microsphere.test.annotation; + +import io.microsphere.annotation.ConfigurationProperty; +import io.microsphere.annotation.Since; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.util.concurrent.TimeUnit.DAYS; + +/** + * The {@link Annotation} for testing + * + * @author Mercy + * @see Annotation + * @since 1.0.0 + */ +@Retention(RUNTIME) +@Target(TYPE) +@Documented +public @interface TestAnnotation { + + boolean z() default false; + + char c() default 'a'; + + byte b() default 1; + + short s() default 2; + + int i() default 3; + + long l() default 4L; + + float f() default 5.0f; + + double d() default 6.0d; + + String string() default "string"; + + Class type() default String.class; + + Class[] types() default {String.class, Integer.class}; + + TimeUnit timeUnit() default DAYS; + + Since since(); + + ConfigurationProperty[] properties() default {}; + +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java new file mode 100644 index 000000000..2b1bcd330 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java @@ -0,0 +1,153 @@ +/* + * 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. + */ +package io.microsphere.test.annotation.processing; + +import io.microsphere.test.service.TestServiceImpl; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.AnnotatedConstruct; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +/** + * Abstract {@link Annotation} Processing Test case + * + * @author Mercy + * @since 1.0.0 + */ +@ExtendWith(CompilerInvocationInterceptor.class) +public abstract class AbstractAnnotationProcessingTest { + + protected static final TypeMirror NULL_TYPE_MIRROR = null; + + protected static final TypeMirror[] EMPTY_TYPE_MIRROR_ARRAY = new TypeMirror[0]; + + protected static final TypeMirror[] NULL_TYPE_MIRROR_ARRAY = null; + + protected static final Collection[] EMPTY_COLLECTION_ARRAY = new Collection[0]; + + protected static final Collection NULL_COLLECTION = null; + + protected static final List NULL_LIST = null; + + protected static final Element NULL_ELEMENT = null; + + protected static final ElementKind NULL_ELEMENT_KIND = null; + + protected static final Element[] EMPTY_ELEMENT_ARRAY = new Element[0]; + + protected static final Element[] NULL_ELEMENT_ARRAY = null; + + protected static final TypeElement NULL_TYPE_ELEMENT = null; + + protected static final Type[] NULL_TYPE_ARRAY = null; + + protected static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; + + protected static final Type NULL_TYPE = null; + + protected static final ProcessingEnvironment NULL_PROCESSING_ENVIRONMENT = null; + + protected static final String NULL_STRING = null; + + protected static final String[] NULL_STRING_ARRAY = null; + + protected static final Class NULL_CLASS = null; + + protected static final Class[] NULL_CLASS_ARRAY = null; + + protected static final AnnotatedConstruct NULL_ANNOTATED_CONSTRUCT = null; + + protected static final Predicate[] NULL_PREDICATE_ARRAY = null; + + protected static final VariableElement NULL_FIELD = null; + + protected static final Modifier NULL_MODIFIER = null; + + protected static final Modifier[] NULL_MODIFIER_ARRAY = null; + + protected static final ExecutableElement NULL_METHOD = null; + + protected static final ExecutableElement[] NULL_METHOD_ARRAY = null; + + protected static final AnnotationMirror NULL_ANNOTATION_MIRROR = null; + + static ThreadLocal testInstanceHolder = new ThreadLocal<>(); + + protected RoundEnvironment roundEnv; + + protected ProcessingEnvironment processingEnv; + + protected Elements elements; + + protected Types types; + + protected Class testClass; + + protected String testClassName; + + protected TypeElement testTypeElement; + + protected TypeMirror testTypeMirror; + + protected DeclaredType testDeclaredType; + + @BeforeEach + final void setUp() { + testInstanceHolder.set(this); + } + + @AfterEach + final void tearDown() { + testInstanceHolder.remove(); + } + + protected void addCompiledClasses(Set> compiledClasses) { + } + + protected void beforeTest() { + this.testClass = TestServiceImpl.class; + this.testClassName = TestServiceImpl.class.getName(); + this.elements = processingEnv.getElementUtils(); + this.testTypeElement = this.elements.getTypeElement(this.testClassName); + this.testTypeMirror = this.testTypeElement.asType(); + this.testDeclaredType = (DeclaredType) testTypeElement.asType(); + } + + protected void afterTest() { + } + +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java new file mode 100644 index 000000000..51511cc5c --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java @@ -0,0 +1,87 @@ +/* + * 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. + */ +package io.microsphere.test.annotation.processing; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.InvocationInterceptor; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import java.lang.reflect.Method; +import java.util.Set; + +import static io.microsphere.constants.SymbolConstants.WILDCARD; +import static javax.lang.model.SourceVersion.latestSupported; + +/** + * {@link AnnotationProcessingTestProcessor} + * + * @author Mercy + * @since 1.0.0 + */ +@SupportedAnnotationTypes(WILDCARD) +public class AnnotationProcessingTestProcessor extends AbstractProcessor { + + private final AbstractAnnotationProcessingTest abstractAnnotationProcessingTest; + + private final InvocationInterceptor.Invocation invocation; + + private final ReflectiveInvocationContext invocationContext; + + private final ExtensionContext extensionContext; + + public AnnotationProcessingTestProcessor(AbstractAnnotationProcessingTest abstractAnnotationProcessingTest, InvocationInterceptor.Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { + this.abstractAnnotationProcessingTest = abstractAnnotationProcessingTest; + this.invocation = invocation; + this.invocationContext = invocationContext; + this.extensionContext = extensionContext; + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!roundEnv.processingOver()) { + prepare(roundEnv); + abstractAnnotationProcessingTest.beforeTest(); + try { + invocation.proceed(); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } finally { + abstractAnnotationProcessingTest.afterTest(); + } + } + return false; + } + + protected void prepare(RoundEnvironment roundEnv) { + abstractAnnotationProcessingTest.roundEnv = roundEnv; + abstractAnnotationProcessingTest.processingEnv = super.processingEnv; + abstractAnnotationProcessingTest.elements = super.processingEnv.getElementUtils(); + abstractAnnotationProcessingTest.types = super.processingEnv.getTypeUtils(); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return latestSupported(); + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java new file mode 100644 index 000000000..296ca9ac3 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java @@ -0,0 +1,59 @@ +/* + * 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. + */ +package io.microsphere.test.annotation.processing; + +import io.microsphere.jdk.tools.compiler.Compiler; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.InvocationInterceptor; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; + +import javax.annotation.processing.Processor; +import java.lang.reflect.Method; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import static io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest.testInstanceHolder; +import static io.microsphere.util.ServiceLoaderUtils.loadServicesList; + + +/** + * {@link InvocationInterceptor} based on Java {@link Compiler} + * + * @author Mercy + * @since 1.0.0 + */ +public class CompilerInvocationInterceptor implements InvocationInterceptor { + + @Override + public void interceptTestMethod(Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) throws Throwable { + Set> compiledClasses = new LinkedHashSet<>(); + AbstractAnnotationProcessingTest abstractAnnotationProcessingTest = testInstanceHolder.get(); + Class testClass = extensionContext.getTestClass().get(); + compiledClasses.add(testClass); + abstractAnnotationProcessingTest.addCompiledClasses(compiledClasses); + Compiler compiler = new Compiler(); + compiler.sourcePaths(testClass); + List processors = new LinkedList<>(loadServicesList(Processor.class, testClass.getClassLoader())); + processors.add(new AnnotationProcessingTestProcessor(abstractAnnotationProcessingTest, invocation, invocationContext, extensionContext)); + compiler.processors(processors.toArray(new Processor[0])); + compiler.compile(compiledClasses.toArray(new Class[0])); + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/Ancestor.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/Ancestor.java new file mode 100644 index 000000000..cf0e07f70 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/Ancestor.java @@ -0,0 +1,38 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +import java.io.Serializable; + +/** + * Ancestor + * + * @author Mercy + * @since 1.0.0 + */ +public class Ancestor implements Serializable { + + private boolean z; + + public boolean isZ() { + return z; + } + + public void setZ(boolean z) { + this.z = z; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/ArrayTypeModel.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/ArrayTypeModel.java new file mode 100644 index 000000000..d9588781e --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/ArrayTypeModel.java @@ -0,0 +1,76 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +/** + * Array Type Model + * + * @author Mercy + * @since 1.0.0 + */ +public class ArrayTypeModel { + + private int[] integers; // Primitive type array + + private String[] strings; // Simple type array + + private PrimitiveTypeModel[] primitiveTypeModels; // Complex type array + + private Model[] models; // Hierarchical Complex type array + + private Color[] colors; // Enum type array + + public int[] getIntegers() { + return integers; + } + + public void setIntegers(int[] integers) { + this.integers = integers; + } + + public String[] getStrings() { + return strings; + } + + public void setStrings(String[] strings) { + this.strings = strings; + } + + public PrimitiveTypeModel[] getPrimitiveTypeModels() { + return primitiveTypeModels; + } + + public void setPrimitiveTypeModels(PrimitiveTypeModel[] primitiveTypeModels) { + this.primitiveTypeModels = primitiveTypeModels; + } + + public Model[] getModels() { + return models; + } + + public void setModels(Model[] models) { + this.models = models; + } + + public Color[] getColors() { + return colors; + } + + public void setColors(Color[] colors) { + this.colors = colors; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/CollectionTypeModel.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/CollectionTypeModel.java new file mode 100644 index 000000000..414d65451 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/CollectionTypeModel.java @@ -0,0 +1,82 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.Queue; +import java.util.Set; + +/** + * {@link Collection} Type Model + * + * @author Mercy + * @since 1.0.0 + */ +public class CollectionTypeModel { + + private Collection strings; // The composite element is simple type + + private List colors; // The composite element is Enum type + + private Queue primitiveTypeModels; // The composite element is POJO type + + private Deque models; // The composite element is hierarchical POJO type + + private Set modelArrays; // The composite element is hierarchical POJO type + + public Collection getStrings() { + return strings; + } + + public void setStrings(Collection strings) { + this.strings = strings; + } + + public List getColors() { + return colors; + } + + public void setColors(List colors) { + this.colors = colors; + } + + public Queue getPrimitiveTypeModels() { + return primitiveTypeModels; + } + + public void setPrimitiveTypeModels(Queue primitiveTypeModels) { + this.primitiveTypeModels = primitiveTypeModels; + } + + public Deque getModels() { + return models; + } + + public void setModels(Deque models) { + this.models = models; + } + + public Set getModelArrays() { + return modelArrays; + } + + public void setModelArrays(Set modelArrays) { + this.modelArrays = modelArrays; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/Color.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/Color.java new file mode 100644 index 000000000..f362a8376 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/Color.java @@ -0,0 +1,47 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +/** + * Color enumeration + * + * @author Mercy + * @since 1.0.0 + */ +public enum Color { + + RED(1), + YELLOW(2), + BLUE(3); + + private final int value; + + Color(int value) { + this.value = value; + } + + @Override + public String toString() { + return "Color{" + + "value=" + value + + "} " + super.toString(); + } + + public int getValue() { + return value; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/ConfigurationPropertyModel.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/ConfigurationPropertyModel.java new file mode 100644 index 000000000..a8e206bd0 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/ConfigurationPropertyModel.java @@ -0,0 +1,85 @@ +/* + * 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. + */ + +package io.microsphere.test.model; + +import io.microsphere.annotation.ConfigurationProperty; + +/** + * {@link ConfigurationProperty} Model + * + * @author Mercy + * @see ConfigurationProperty + * @since 1.0.0 + */ +public class ConfigurationPropertyModel { + + @ConfigurationProperty(name = "microsphere.annotation.processor.model.name") + private String name; + + @ConfigurationProperty(name = "microsphere.annotation.processor.model.type") + private Class type; + + @ConfigurationProperty(name = "microsphere.annotation.processor.model.default-value") + private String defaultValue; + + @ConfigurationProperty(name = "microsphere.annotation.processor.model.required") + private boolean required; + + @ConfigurationProperty(name = "microsphere.annotation.processor.model.description") + private String description; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Class getType() { + return type; + } + + public void setType(Class type) { + this.type = type; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public boolean isRequired() { + return required; + } + + public void setRequired(boolean required) { + this.required = required; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/MapTypeModel.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/MapTypeModel.java new file mode 100644 index 000000000..bf051c5b9 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/MapTypeModel.java @@ -0,0 +1,82 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * {@link Map} Type model + * + * @author Mercy + * @since 1.0.0 + */ +public class MapTypeModel { + + private Map strings; // The composite element is simple type + + private SortedMap colors; // The composite element is Enum type + + private NavigableMap primitiveTypeModels; // The composite element is POJO type + + private HashMap models; // The composite element is hierarchical POJO type + + private TreeMap modelArrays; // The composite element is hierarchical POJO type + + public Map getStrings() { + return strings; + } + + public void setStrings(Map strings) { + this.strings = strings; + } + + public SortedMap getColors() { + return colors; + } + + public void setColors(SortedMap colors) { + this.colors = colors; + } + + public NavigableMap getPrimitiveTypeModels() { + return primitiveTypeModels; + } + + public void setPrimitiveTypeModels(NavigableMap primitiveTypeModels) { + this.primitiveTypeModels = primitiveTypeModels; + } + + public HashMap getModels() { + return models; + } + + public void setModels(HashMap models) { + this.models = models; + } + + public TreeMap getModelArrays() { + return modelArrays; + } + + public void setModelArrays(TreeMap modelArrays) { + this.modelArrays = modelArrays; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/Model.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/Model.java new file mode 100644 index 000000000..5d972f3c9 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/Model.java @@ -0,0 +1,90 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + +/** + * Model Object + * + * @author Mercy + * @since 1.0.0 + */ +public class Model extends Parent { + + private float f; + + private double d; + + private TimeUnit tu; + + private String str; + + private BigInteger bi; + + private BigDecimal bd; + + public float getF() { + return f; + } + + public void setF(float f) { + this.f = f; + } + + public double getD() { + return d; + } + + public void setD(double d) { + this.d = d; + } + + public TimeUnit getTu() { + return tu; + } + + public void setTu(TimeUnit tu) { + this.tu = tu; + } + + public String getStr() { + return str; + } + + public void setStr(String str) { + this.str = str; + } + + public BigInteger getBi() { + return bi; + } + + public void setBi(BigInteger bi) { + this.bi = bi; + } + + public BigDecimal getBd() { + return bd; + } + + public void setBd(BigDecimal bd) { + this.bd = bd; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/Parent.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/Parent.java new file mode 100644 index 000000000..f5e85fe1e --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/Parent.java @@ -0,0 +1,66 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +/** + * Parent + * + * @author Mercy + * @since 1.0.0 + */ +public class Parent extends Ancestor { + + private byte b; + + private short s; + + private int i; + + private long l; + + public byte getB() { + return b; + } + + public void setB(byte b) { + this.b = b; + } + + public short getS() { + return s; + } + + public void setS(short s) { + this.s = s; + } + + public int getI() { + return i; + } + + public void setI(int i) { + this.i = i; + } + + public long getL() { + return l; + } + + public void setL(long l) { + this.l = l; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/PrimitiveTypeModel.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/PrimitiveTypeModel.java new file mode 100644 index 000000000..5a1065fc7 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/PrimitiveTypeModel.java @@ -0,0 +1,74 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +/** + * Primitive Type model + * + * @author Mercy + * @since 1.0.0 + */ +public class PrimitiveTypeModel { + + private boolean z; + + private byte b; + + private char c; + + private short s; + + private int i; + + private long l; + + private float f; + + private double d; + + public boolean isZ() { + return z; + } + + public byte getB() { + return b; + } + + public char getC() { + return c; + } + + public short getS() { + return s; + } + + public int getI() { + return i; + } + + public long getL() { + return l; + } + + public float getF() { + return f; + } + + public double getD() { + return d; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/SimpleTypeModel.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/SimpleTypeModel.java new file mode 100644 index 000000000..86a8b6b8b --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/SimpleTypeModel.java @@ -0,0 +1,162 @@ +/* + * 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. + */ +package io.microsphere.test.model; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; + +/** + * Simple Type model + * + * @author Mercy + * @since 1.0.0 + */ +public class SimpleTypeModel { + + private Void v; + + private Boolean z; + + private Character c; + + private Byte b; + + private Short s; + + private Integer i; + + private Long l; + + private Float f; + + private Double d; + + private String str; + + private BigDecimal bd; + + private BigInteger bi; + + private Date dt; + + private int invalid; + + public Void getV() { + return v; + } + + public void setV(Void v) { + this.v = v; + } + + public Boolean getZ() { + return z; + } + + public void setZ(Boolean z) { + this.z = z; + } + + public Character getC() { + return c; + } + + public void setC(Character c) { + this.c = c; + } + + public Byte getB() { + return b; + } + + public void setB(Byte b) { + this.b = b; + } + + public Short getS() { + return s; + } + + public void setS(Short s) { + this.s = s; + } + + public Integer getI() { + return i; + } + + public void setI(Integer i) { + this.i = i; + } + + public Long getL() { + return l; + } + + public void setL(Long l) { + this.l = l; + } + + public Float getF() { + return f; + } + + public void setF(Float f) { + this.f = f; + } + + public Double getD() { + return d; + } + + public void setD(Double d) { + this.d = d; + } + + public String getStr() { + return str; + } + + public void setStr(String str) { + this.str = str; + } + + public BigDecimal getBd() { + return bd; + } + + public void setBd(BigDecimal bd) { + this.bd = bd; + } + + public BigInteger getBi() { + return bi; + } + + public void setBi(BigInteger bi) { + this.bi = bi; + } + + public Date getDt() { + return dt; + } + + public void setDt(Date dt) { + this.dt = dt; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/model/StringArrayList.java b/microsphere-java-test/src/main/java/io/microsphere/test/model/StringArrayList.java new file mode 100644 index 000000000..e7c115a09 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/model/StringArrayList.java @@ -0,0 +1,30 @@ +/* + * 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. + */ + +package io.microsphere.test.model; + +import java.util.ArrayList; + +/** + * String type {@link ArrayList} + * + * @author Mercy + * @see ArrayList + * @since 1.0.0 + */ +public class StringArrayList extends ArrayList { +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/service/DefaultTestService.java b/microsphere-java-test/src/main/java/io/microsphere/test/service/DefaultTestService.java new file mode 100644 index 000000000..4ca1d694b --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/service/DefaultTestService.java @@ -0,0 +1,58 @@ +/* + * 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. + */ +package io.microsphere.test.service; + + +import io.microsphere.test.model.Model; + +import java.util.concurrent.TimeUnit; + +/** + * {@link TestService} Implementation + * + * @author Mercy + * @since 1.0.0 + */ +public class DefaultTestService implements TestService { + + private String name; + + @Override + public String echo(String message) { + return "[ECHO] " + message; + } + + @Override + public Model model(Model model) { + return model; + } + + @Override + public String testPrimitive(boolean z, int i) { + return null; + } + + @Override + public Model testEnum(TimeUnit timeUnit) { + return null; + } + + @Override + public String testArray(String[] strArray, int[] intArray, Model[] modelArray) { + return null; + } +} diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/service/GenericTestService.java b/microsphere-java-test/src/main/java/io/microsphere/test/service/GenericTestService.java new file mode 100644 index 000000000..d4a81a787 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/service/GenericTestService.java @@ -0,0 +1,33 @@ +/* + * 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. + */ +package io.microsphere.test.service; + + +import java.util.EventListener; + +/** + * {@link TestService} Implementation + * + * @author Mercy + * @since 1.0.0 + */ +public class GenericTestService extends DefaultTestService implements TestService, EventListener { + @Override + public String echo(String message) { + return "[ECHO] " + message; + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/service/TestService.java b/microsphere-java-test/src/main/java/io/microsphere/test/service/TestService.java new file mode 100644 index 000000000..22839bc80 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/service/TestService.java @@ -0,0 +1,56 @@ +/* + * 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. + */ +package io.microsphere.test.service; + + +import io.microsphere.test.model.Model; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import java.util.concurrent.TimeUnit; + +/** + * Test Service + * + * @author Mercy + * @since 1.0.0 + */ +@Path("/echo") +public interface TestService { + + @GET + String echo(@PathParam("message") @DefaultValue("mercyblitz") String message); + + @POST + Model model(@PathParam("model") Model model); + + // Test primitive + @PUT + String testPrimitive(boolean z, int i); + + // Test enumeration + @PUT + Model testEnum(TimeUnit timeUnit); + + // Test Array + @GET + String testArray(String[] strArray, int[] intArray, Model[] modelArray); +} diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/service/TestServiceImpl.java b/microsphere-java-test/src/main/java/io/microsphere/test/service/TestServiceImpl.java new file mode 100644 index 000000000..a97658884 --- /dev/null +++ b/microsphere-java-test/src/main/java/io/microsphere/test/service/TestServiceImpl.java @@ -0,0 +1,101 @@ +/* + * 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. + */ +package io.microsphere.test.service; + +import io.microsphere.annotation.ConfigurationProperty; +import io.microsphere.annotation.Since; +import io.microsphere.test.annotation.TestAnnotation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScans; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; + +import javax.xml.ws.ServiceMode; +import java.io.Serializable; + +import static java.util.concurrent.TimeUnit.HOURS; +import static org.springframework.context.annotation.FilterType.ASPECTJ; +import static org.springframework.context.annotation.ScopedProxyMode.INTERFACES; + +/** + * @author Mercy + * @since 1.0.0 + */ +@Service("testService") +@ServiceMode +@ComponentScans(value = { + @ComponentScan( + basePackages = "io.microsphere.annotation.processor.model", + scopedProxy = INTERFACES + ), + @ComponentScan( + basePackages = "io.microsphere.annotation.processor.util", + includeFilters = { + @ComponentScan.Filter( + type = ASPECTJ, + classes = {Object.class, CharSequence.class} + ) + }) +}) +@TestAnnotation( + z = true, + c = 'b', + b = 1, + s = 1, + i = 1, + l = 1, + f = 1, + d = 1, + string = "testService", + type = GenericTestService.class, + types = {TestService.class, AutoCloseable.class, Serializable.class}, + timeUnit = HOURS, + since = @Since("1.0.0"), + properties = { + @ConfigurationProperty(name = "key", type = String.class, defaultValue = "default-value", required = true, description = "description"), + @ConfigurationProperty(name = "key2", type = Integer.class, defaultValue = "default-value2", required = true, description = "description2"), + @ConfigurationProperty(name = "key3", type = Class.class, defaultValue = "default-value3", required = true, description = "description3") + } +) +public class TestServiceImpl extends GenericTestService implements TestService, AutoCloseable, Serializable { + + @Autowired + ApplicationContext context; + + Environment environment; + + public TestServiceImpl() { + this(null); + } + + public TestServiceImpl(@Autowired Environment environment) { + this.environment = environment; + } + + @Override + @Cacheable(cacheNames = {"cache-1", "cache-2"}) + public String echo(String message) { + return "[ECHO] " + message; + } + + @Override + public void close() throws Exception { + } +} diff --git a/microsphere-java-test/src/main/resources/logback-test.xml b/microsphere-java-test/src/main/resources/logback-test.xml new file mode 100644 index 000000000..e3375b74c --- /dev/null +++ b/microsphere-java-test/src/main/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + + ${ENCODER_PATTERN} + + + + + + + + \ No newline at end of file diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java new file mode 100644 index 000000000..935b1e5f5 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java @@ -0,0 +1,71 @@ +/* + * 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. + */ + +package io.microsphere.test.annotation.processing; + +import org.junit.jupiter.api.Test; + +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; +import java.lang.reflect.Type; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * {@link AbstractAnnotationProcessingTest} Test + * + * @author Mercy + * @see AbstractAnnotationProcessingTest + * @since 1.0.0 + */ +public class AnnotationProcessingTest extends AbstractAnnotationProcessingTest { + + @Test + void test() { + assertNull(NULL_TYPE_MIRROR); + assertArrayEquals(new TypeMirror[0], EMPTY_TYPE_MIRROR_ARRAY); + assertNull(NULL_TYPE_MIRROR_ARRAY); + assertArrayEquals(new Collection[0], EMPTY_COLLECTION_ARRAY); + assertNull(NULL_COLLECTION); + assertNull(NULL_LIST); + assertNull(NULL_ELEMENT); + assertNull(NULL_ELEMENT_KIND); + assertArrayEquals(new Element[0], EMPTY_ELEMENT_ARRAY); + assertNull(NULL_ELEMENT_ARRAY); + assertNull(NULL_TYPE_ELEMENT); + assertNull(NULL_TYPE_ARRAY); + assertArrayEquals(new Type[0], EMPTY_TYPE_ARRAY); + assertNull(NULL_TYPE); + assertNull(NULL_PROCESSING_ENVIRONMENT); + assertNull(NULL_STRING); + assertNull(NULL_STRING_ARRAY); + assertNull(NULL_CLASS); + assertNull(NULL_CLASS_ARRAY); + assertNull(NULL_ANNOTATED_CONSTRUCT); + assertNull(NULL_PREDICATE_ARRAY); + assertNull(NULL_FIELD); + assertNull(NULL_MODIFIER); + assertNull(NULL_MODIFIER_ARRAY); + assertNull(NULL_METHOD); + assertNull(NULL_METHOD_ARRAY); + assertNull(NULL_ANNOTATION_MIRROR); + assertNotNull(testInstanceHolder); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/TestProcessor.java b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/TestProcessor.java new file mode 100644 index 000000000..e1ad024a5 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/TestProcessor.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +package io.microsphere.test.annotation.processing; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import java.util.Set; + +import static javax.lang.model.SourceVersion.latestSupported; + +/** + * Test {@link Processor} + * + * @author Mercy + * @see Processor + * @since 1.0.0 + */ +@SupportedAnnotationTypes("*") +public class TestProcessor extends AbstractProcessor { + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return true; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return latestSupported(); + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/AncestorTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/AncestorTest.java new file mode 100644 index 000000000..b96c57b36 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/AncestorTest.java @@ -0,0 +1,81 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the Ancestor class + */ +class AncestorTest { + + private Ancestor ancestor; + + @BeforeEach + void setUp() { + ancestor = new Ancestor(); + } + + @Test + void testDefaultConstructor() { + // Verify that the default value of z is false + assertFalse(ancestor.isZ(), "Default value of z should be false"); + } + + @Test + void testSetZTrue() { + // Set z to true and verify + ancestor.setZ(true); + assertTrue(ancestor.isZ(), "Value of z should be true after setting it to true"); + } + + @Test + void testSetZFalse() { + // Explicitly set z to false and verify + ancestor.setZ(false); + assertFalse(ancestor.isZ(), "Value of z should be false after setting it to false"); + } + + @Test + void testSetZToggle() { + // Test toggling the value from default false to true and back to false + assertFalse(ancestor.isZ(), "Initial value should be false"); + + ancestor.setZ(true); + assertTrue(ancestor.isZ(), "Value should be true after first toggle"); + + ancestor.setZ(false); + assertFalse(ancestor.isZ(), "Value should be false after second toggle"); + } + + @Test + void testMultipleInstanceIndependence() { + // Create two instances and verify they maintain independent state + Ancestor ancestor1 = new Ancestor(); + Ancestor ancestor2 = new Ancestor(); + + // Initially both should have false + assertFalse(ancestor1.isZ(), "First instance should initially be false"); + assertFalse(ancestor2.isZ(), "Second instance should initially be false"); + + // Modify only the first instance + ancestor1.setZ(true); + + // Verify that only the first instance changed + assertTrue(ancestor1.isZ(), "First instance should be true after modification"); + assertFalse(ancestor2.isZ(), "Second instance should remain false"); + } + + @Test + void testSerializableImplementation() { + // Test that the class can be instantiated and used as a Serializable object + // This test verifies basic functionality without actual serialization + ancestor.setZ(true); + assertTrue(ancestor.isZ(), "Should properly handle boolean value when used as Serializable"); + + ancestor.setZ(false); + assertFalse(ancestor.isZ(), "Should properly handle boolean value when used as Serializable"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/ArrayTypeModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/ArrayTypeModelTest.java new file mode 100644 index 000000000..ebf9644af --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/ArrayTypeModelTest.java @@ -0,0 +1,122 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static io.microsphere.test.model.Color.BLUE; +import static io.microsphere.test.model.Color.RED; +import static io.microsphere.test.model.Color.YELLOW; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * Unit tests for the ArrayTypeModel class + */ +class ArrayTypeModelTest { + + private ArrayTypeModel model; + + @BeforeEach + void setUp() { + model = new ArrayTypeModel(); + } + + @Test + void testDefaultValues() { + // Verify all array fields are null by default + assertNull(model.getIntegers(), "integers array should be null by default"); + assertNull(model.getStrings(), "strings array should be null by default"); + assertNull(model.getPrimitiveTypeModels(), "primitiveTypeModels array should be null by default"); + assertNull(model.getModels(), "models array should be null by default"); + assertNull(model.getColors(), "colors array should be null by default"); + } + + @Test + void testSetGetIntegers() { + int[] testArray = {1, 2, 3, 4, 5}; + + model.setIntegers(testArray); + assertSame(testArray, model.getIntegers(), "Should return the same array reference that was set"); + + // Verify the contents match + assertArrayEquals(testArray, model.getIntegers(), "Array contents should match the set value"); + } + + @Test + void testSetGetStrings() { + String[] testArray = {"hello", "world", "test"}; + + model.setStrings(testArray); + assertSame(testArray, model.getStrings(), "Should return the same array reference that was set"); + + // Verify the contents match + assertArrayEquals(testArray, model.getStrings(), "Array contents should match the set value"); + } + + @Test + void testSetGetPrimitiveTypeModels() { + PrimitiveTypeModel[] testArray = {new PrimitiveTypeModel(), new PrimitiveTypeModel()}; + + model.setPrimitiveTypeModels(testArray); + assertSame(testArray, model.getPrimitiveTypeModels(), "Should return the same array reference that was set"); + + // Verify the contents match + assertArrayEquals(testArray, model.getPrimitiveTypeModels(), "Array contents should match the set value"); + } + + @Test + void testSetGetModels() { + Model[] testArray = {new Model(), new Model()}; + + model.setModels(testArray); + assertSame(testArray, model.getModels(), "Should return the same array reference that was set"); + + // Verify the contents match + assertArrayEquals(testArray, model.getModels(), "Array contents should match the set value"); + } + + @Test + void testSetGetColors() { + Color[] testArray = {RED, BLUE, YELLOW}; + + model.setColors(testArray); + assertSame(testArray, model.getColors(), "Should return the same array reference that was set"); + + // Verify the contents match + assertArrayEquals(testArray, model.getColors(), "Array contents should match the set value"); + } + + @Test + void testSetNullValues() { + // Test setting each field to null + model.setIntegers(null); + assertNull(model.getIntegers(), "integers should be null after setting to null"); + + model.setStrings(null); + assertNull(model.getStrings(), "strings should be null after setting to null"); + + model.setPrimitiveTypeModels(null); + assertNull(model.getPrimitiveTypeModels(), "primitiveTypeModels should be null after setting to null"); + + model.setModels(null); + assertNull(model.getModels(), "models should be null after setting to null"); + + model.setColors(null); + assertNull(model.getColors(), "colors should be null after setting to null"); + } + + @Test + void testArrayMutability() { + int[] originalArray = {1, 2, 3}; + model.setIntegers(originalArray); + + // Modify the original array + originalArray[0] = 999; + + // Check if the change is reflected in the getter result + assertEquals(999, model.getIntegers()[0], + "Changes to the original array should be reflected since arrays are passed by reference"); + } +} \ No newline at end of file diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/CollectionTypeModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/CollectionTypeModelTest.java new file mode 100644 index 000000000..af3ae9098 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/CollectionTypeModelTest.java @@ -0,0 +1,138 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; + +import static io.microsphere.test.model.Color.BLUE; +import static io.microsphere.test.model.Color.RED; +import static io.microsphere.test.model.Color.YELLOW; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the CollectionTypeModel class + */ +class CollectionTypeModelTest { + + private CollectionTypeModel model; + + @BeforeEach + void setUp() { + model = new CollectionTypeModel(); + } + + @Test + void testDefaultValues() { + // Verify all collection fields are null by default + assertNull(model.getStrings(), "strings collection should be null by default"); + assertNull(model.getColors(), "colors list should be null by default"); + assertNull(model.getPrimitiveTypeModels(), "primitiveTypeModels queue should be null by default"); + assertNull(model.getModels(), "models deque should be null by default"); + assertNull(model.getModelArrays(), "modelArrays set should be null by default"); + } + + @Test + void testSetGetStrings() { + Collection testCollection = asList("hello", "world", "test"); + + model.setStrings(testCollection); + assertSame(testCollection, model.getStrings(), "Should return the same collection reference that was set"); + + // Verify the contents match + assertEquals(testCollection, model.getStrings(), "Collection contents should match the set value"); + } + + @Test + void testSetGetColors() { + List testList = asList(RED, BLUE, YELLOW); + + model.setColors(testList); + assertSame(testList, model.getColors(), "Should return the same list reference that was set"); + + // Verify the contents match + assertEquals(testList, model.getColors(), "List contents should match the set value"); + } + + @Test + void testSetGetPrimitiveTypeModels() { + Queue testQueue = new LinkedList<>(); + testQueue.add(new PrimitiveTypeModel()); + testQueue.add(new PrimitiveTypeModel()); + + model.setPrimitiveTypeModels(testQueue); + assertSame(testQueue, model.getPrimitiveTypeModels(), "Should return the same queue reference that was set"); + + // Verify the contents match + assertEquals(testQueue, model.getPrimitiveTypeModels(), "Queue contents should match the set value"); + } + + @Test + void testSetGetModels() { + Deque testDeque = new LinkedList<>(); + testDeque.add(new Model()); + testDeque.add(new Model()); + + model.setModels(testDeque); + assertSame(testDeque, model.getModels(), "Should return the same deque reference that was set"); + + // Verify the contents match + assertEquals(testDeque, model.getModels(), "Deque contents should match the set value"); + } + + @Test + void testSetGetModelArrays() { + Set testSet = new HashSet<>(); + testSet.add(new Model[]{new Model(), new Model()}); + testSet.add(new Model[]{new Model()}); + + model.setModelArrays(testSet); + assertSame(testSet, model.getModelArrays(), "Should return the same set reference that was set"); + + // Verify the contents match + assertEquals(testSet, model.getModelArrays(), "Set contents should match the set value"); + } + + @Test + void testSetNullValues() { + // Test setting each field to null + model.setStrings(null); + assertNull(model.getStrings(), "strings should be null after setting to null"); + + model.setColors(null); + assertNull(model.getColors(), "colors should be null after setting to null"); + + model.setPrimitiveTypeModels(null); + assertNull(model.getPrimitiveTypeModels(), "primitiveTypeModels should be null after setting to null"); + + model.setModels(null); + assertNull(model.getModels(), "models should be null after setting to null"); + + model.setModelArrays(null); + assertNull(model.getModelArrays(), "modelArrays should be null after setting to null"); + } + + @Test + void testCollectionMutability() { + List originalList = new ArrayList<>(asList(RED, BLUE)); + model.setColors(originalList); + + // Modify the original collection + originalList.add(RED); + + // Check if the change is reflected in the getter result + assertTrue(model.getColors().contains(RED), + "Changes to the original collection should be reflected since collections are passed by reference"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/ColorTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/ColorTest.java new file mode 100644 index 000000000..9f8f0cfde --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/ColorTest.java @@ -0,0 +1,103 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.Test; + +import static io.microsphere.test.model.Color.BLUE; +import static io.microsphere.test.model.Color.RED; +import static io.microsphere.test.model.Color.YELLOW; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the Color enum + */ +class ColorTest { + + @Test + void testEnumValues() { + // Verify that all enum constants exist and are accessible + assertNotNull(RED, "RED color should exist"); + assertNotNull(YELLOW, "YELLOW color should exist"); + assertNotNull(BLUE, "BLUE color should exist"); + } + + @Test + void testGetValueMethod() { + // Verify the value associated with each color + assertEquals(1, RED.getValue(), "RED should have value 1"); + assertEquals(2, YELLOW.getValue(), "YELLOW should have value 2"); + assertEquals(3, BLUE.getValue(), "BLUE should have value 3"); + } + + @Test + void testToStringMethod() { + // Verify the string representation of each color + String redString = RED.toString(); + assertTrue(redString.contains("Color{value=1}"), "RED toString should contain value=1"); + assertTrue(redString.endsWith("RED"), "RED toString should end with RED"); + + String yellowString = YELLOW.toString(); + assertTrue(yellowString.contains("Color{value=2}"), "YELLOW toString should contain value=2"); + assertTrue(yellowString.endsWith("YELLOW"), "YELLOW toString should end with YELLOW"); + + String blueString = BLUE.toString(); + assertTrue(blueString.contains("Color{value=3}"), "BLUE toString should contain value=3"); + assertTrue(blueString.endsWith("BLUE"), "BLUE toString should end with BLUE"); + } + + @Test + void testEnumOrdinality() { + // Verify that enum constants have expected ordinal positions + assertEquals(0, RED.ordinal(), "RED should be at ordinal position 0"); + assertEquals(1, YELLOW.ordinal(), "YELLOW should be at ordinal position 1"); + assertEquals(2, BLUE.ordinal(), "BLUE should be at ordinal position 2"); + } + + @Test + void testEnumName() { + // Verify that enum constants have correct names + assertEquals("RED", RED.name(), "RED name should be 'RED'"); + assertEquals("YELLOW", YELLOW.name(), "YELLOW name should be 'YELLOW'"); + assertEquals("BLUE", BLUE.name(), "BLUE name should be 'BLUE'"); + } + + @Test + void testEnumEquality() { + // Verify that enum equality works correctly + assertEquals(RED, RED, "RED should equal itself"); + assertEquals(YELLOW, YELLOW, "YELLOW should equal itself"); + assertEquals(BLUE, BLUE, "BLUE should equal itself"); + + assertNotEquals(RED, BLUE, "RED should not equal BLUE"); + assertNotEquals(RED, YELLOW, "RED should not equal YELLOW"); + assertNotEquals(BLUE, YELLOW, "BLUE should not equal YELLOW"); + } + + @Test + void testValueImmutability() { + // Verify that the value cannot be changed (as it's final) + int redValue = RED.getValue(); + int yellowValue = YELLOW.getValue(); + int blueValue = BLUE.getValue(); + + // Values should remain constant across multiple calls + assertEquals(redValue, RED.getValue(), "RED value should be immutable"); + assertEquals(yellowValue, YELLOW.getValue(), "YELLOW value should be immutable"); + assertEquals(blueValue, BLUE.getValue(), "BLUE value should be immutable"); + } + + @Test + void testAllEnumConstantsExist() { + // Verify that we can retrieve all enum constants + Color[] allColors = Color.values(); + assertEquals(3, allColors.length, "There should be exactly 3 color constants"); + + // Verify that all expected colors are present + assertTrue(asList(allColors).contains(RED), "All colors should include RED"); + assertTrue(asList(allColors).contains(YELLOW), "All colors should include YELLOW"); + assertTrue(asList(allColors).contains(BLUE), "All colors should include BLUE"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/ConfigurationPropertyModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/ConfigurationPropertyModelTest.java new file mode 100644 index 000000000..4218d3d43 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/ConfigurationPropertyModelTest.java @@ -0,0 +1,152 @@ +package io.microsphere.test.model; + +import io.microsphere.annotation.ConfigurationProperty; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the ConfigurationPropertyModel class + */ +class ConfigurationPropertyModelTest { + + private ConfigurationPropertyModel model; + + @BeforeEach + void setUp() { + model = new ConfigurationPropertyModel(); + } + + @Test + void testDefaultValues() { + // Verify all fields have their default values + assertNull(model.getName(), "name should be null by default"); + assertNull(model.getType(), "type should be null by default"); + assertNull(model.getDefaultValue(), "defaultValue should be null by default"); + assertFalse(model.isRequired(), "required should be false by default"); + assertNull(model.getDescription(), "description should be null by default"); + } + + @Test + void testSetNameAndGet() { + String testName = "test.config.property"; + model.setName(testName); + assertEquals(testName, model.getName(), "getName should return the set value"); + } + + @Test + void testSetTypeAndGet() { + Class testType = String.class; + model.setType(testType); + assertEquals(testType, model.getType(), "getType should return the set value"); + } + + @Test + void testSetDefaultValueAndGet() { + String testDefaultValue = "default_value"; + model.setDefaultValue(testDefaultValue); + assertEquals(testDefaultValue, model.getDefaultValue(), "getDefaultValue should return the set value"); + } + + @Test + void testSetRequiredAndGet() { + model.setRequired(true); + assertTrue(model.isRequired(), "isRequired should return true after setting to true"); + + model.setRequired(false); + assertFalse(model.isRequired(), "isRequired should return false after setting to false"); + } + + @Test + void testSetDescriptionAndGet() { + String testDescription = "This is a test configuration property"; + model.setDescription(testDescription); + assertEquals(testDescription, model.getDescription(), "getDescription should return the set value"); + } + + @Test + void testConfigurationPropertyAnnotations() { + // Test that the ConfigurationProperty annotations are present on the fields + java.lang.reflect.Field[] fields = ConfigurationPropertyModel.class.getDeclaredFields(); + + boolean hasNameAnnotation = false; + boolean hasTypeAnnotation = false; + boolean hasDefaultValueAnnotation = false; + boolean hasRequiredAnnotation = false; + boolean hasDescriptionAnnotation = false; + + for (java.lang.reflect.Field field : fields) { + if (field.isAnnotationPresent(ConfigurationProperty.class)) { + ConfigurationProperty annotation = field.getAnnotation(ConfigurationProperty.class); + + switch (field.getName()) { + case "name": + if ("microsphere.annotation.processor.model.name".equals(annotation.name())) { + hasNameAnnotation = true; + } + break; + case "type": + if ("microsphere.annotation.processor.model.type".equals(annotation.name())) { + hasTypeAnnotation = true; + } + break; + case "defaultValue": + if ("microsphere.annotation.processor.model.default-value".equals(annotation.name())) { + hasDefaultValueAnnotation = true; + } + break; + case "required": + if ("microsphere.annotation.processor.model.required".equals(annotation.name())) { + hasRequiredAnnotation = true; + } + break; + case "description": + if ("microsphere.annotation.processor.model.description".equals(annotation.name())) { + hasDescriptionAnnotation = true; + } + break; + } + } + } + + assertTrue(hasNameAnnotation, "name field should have ConfigurationProperty annotation with correct name"); + assertTrue(hasTypeAnnotation, "type field should have ConfigurationProperty annotation with correct name"); + assertTrue(hasDefaultValueAnnotation, "defaultValue field should have ConfigurationProperty annotation with correct name"); + assertTrue(hasRequiredAnnotation, "required field should have ConfigurationProperty annotation with correct name"); + assertTrue(hasDescriptionAnnotation, "description field should have ConfigurationProperty annotation with correct name"); + } + + @Test + void testSetAllProperties() { + // Test setting all properties and verifying they are returned correctly + model.setName("full.test.name"); + model.setType(Integer.class); + model.setDefaultValue("42"); + model.setRequired(true); + model.setDescription("Complete test configuration"); + + assertEquals("full.test.name", model.getName(), "Name should match set value"); + assertEquals(Integer.class, model.getType(), "Type should match set value"); + assertEquals("42", model.getDefaultValue(), "DefaultValue should match set value"); + assertTrue(model.isRequired(), "Required should match set value"); + assertEquals("Complete test configuration", model.getDescription(), "Description should match set value"); + } + + @Test + void testNullValueHandling() { + // Test setting fields to null + model.setName(null); + model.setType(null); + model.setDefaultValue(null); + model.setDescription(null); + + assertNull(model.getName(), "Name should be null"); + assertNull(model.getType(), "Type should be null"); + assertNull(model.getDefaultValue(), "DefaultValue should be null"); + assertNull(model.getDescription(), "Description should be null"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/MapTypeModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/MapTypeModelTest.java new file mode 100644 index 000000000..9640d802c --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/MapTypeModelTest.java @@ -0,0 +1,138 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; + +import static io.microsphere.test.model.Color.BLUE; +import static io.microsphere.test.model.Color.RED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the MapTypeModel class + */ +class MapTypeModelTest { + + private MapTypeModel model; + + @BeforeEach + void setUp() { + model = new MapTypeModel(); + } + + @Test + void testDefaultValues() { + // Verify all map fields are null by default + assertNull(model.getStrings(), "strings map should be null by default"); + assertNull(model.getColors(), "colors sorted map should be null by default"); + assertNull(model.getPrimitiveTypeModels(), "primitiveTypeModels navigable map should be null by default"); + assertNull(model.getModels(), "models hash map should be null by default"); + assertNull(model.getModelArrays(), "modelArrays tree map should be null by default"); + } + + @Test + void testSetGetStrings() { + Map testMap = new HashMap<>(); + testMap.put("key1", "value1"); + testMap.put("key2", "value2"); + + model.setStrings(testMap); + assertSame(testMap, model.getStrings(), "Should return the same map reference that was set"); + + // Verify the contents match + assertEquals(testMap, model.getStrings(), "Map contents should match the set value"); + } + + @Test + void testSetGetColors() { + SortedMap testMap = new TreeMap<>(); + testMap.put("red_key", RED); + testMap.put("blue_key", BLUE); + + model.setColors(testMap); + assertSame(testMap, model.getColors(), "Should return the same map reference that was set"); + + // Verify the contents match + assertEquals(testMap, model.getColors(), "SortedMap contents should match the set value"); + } + + @Test + void testSetGetPrimitiveTypeModels() { + NavigableMap testMap = new TreeMap<>(); + testMap.put(RED, new PrimitiveTypeModel()); + testMap.put(BLUE, new PrimitiveTypeModel()); + + model.setPrimitiveTypeModels(testMap); + assertSame(testMap, model.getPrimitiveTypeModels(), "Should return the same map reference that was set"); + + // Verify the contents match + assertEquals(testMap, model.getPrimitiveTypeModels(), "NavigableMap contents should match the set value"); + } + + @Test + void testSetGetModels() { + HashMap testMap = new HashMap<>(); + testMap.put("model1", new Model()); + testMap.put("model2", new Model()); + + model.setModels(testMap); + assertSame(testMap, model.getModels(), "Should return the same map reference that was set"); + + // Verify the contents match + assertEquals(testMap, model.getModels(), "HashMap contents should match the set value"); + } + + @Test + void testSetGetModelArrays() { + TreeMap testMap = new TreeMap<>((a, b) -> 1); // Using custom comparator to avoid issues with PrimitiveTypeModel not implementing Comparable + testMap.put(new PrimitiveTypeModel(), new Model[]{new Model()}); + testMap.put(new PrimitiveTypeModel(), new Model[]{new Model(), new Model()}); + + model.setModelArrays(testMap); + assertSame(testMap, model.getModelArrays(), "Should return the same map reference that was set"); + + // Verify the contents match + assertEquals(testMap, model.getModelArrays(), "TreeMap contents should match the set value"); + } + + @Test + void testSetNullValues() { + // Test setting each field to null + model.setStrings(null); + assertNull(model.getStrings(), "strings should be null after setting to null"); + + model.setColors(null); + assertNull(model.getColors(), "colors should be null after setting to null"); + + model.setPrimitiveTypeModels(null); + assertNull(model.getPrimitiveTypeModels(), "primitiveTypeModels should be null after setting to null"); + + model.setModels(null); + assertNull(model.getModels(), "models should be null after setting to null"); + + model.setModelArrays(null); + assertNull(model.getModelArrays(), "modelArrays should be null after setting to null"); + } + + @Test + void testMapMutability() { + Map originalMap = new HashMap<>(); + originalMap.put("initial", "value"); + model.setStrings(originalMap); + + // Modify the original map + originalMap.put("added", "new_value"); + + // Check if the change is reflected in the getter result + assertTrue(model.getStrings().containsKey("added"), + "Changes to the original map should be reflected since maps are passed by reference"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/ModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/ModelTest.java new file mode 100644 index 000000000..5a9b63b28 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/ModelTest.java @@ -0,0 +1,116 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the Model class + */ +class ModelTest { + + private Model model; + + @BeforeEach + void setUp() { + model = new Model(); + } + + @Test + void testDefaultValues() { + // Verify all primitive fields have their default values + assertEquals(0.0f, model.getF(), "float field f should be 0.0f by default"); + assertEquals(0.0d, model.getD(), "double field d should be 0.0d by default"); + + // Verify all object fields are null by default + assertNull(model.getTu(), "TimeUnit field tu should be null by default"); + assertNull(model.getStr(), "String field str should be null by default"); + assertNull(model.getBi(), "BigInteger field bi should be null by default"); + assertNull(model.getBd(), "BigDecimal field bd should be null by default"); + } + + @Test + void testSetGetFloat() { + float testValue = 3.14f; + model.setF(testValue); + assertEquals(testValue, model.getF(), "getF should return the set value"); + } + + @Test + void testSetGetDouble() { + double testValue = 2.71828d; + model.setD(testValue); + assertEquals(testValue, model.getD(), "getD should return the set value"); + } + + @Test + void testSetGetTimeUnit() { + TimeUnit testValue = TimeUnit.SECONDS; + model.setTu(testValue); + assertEquals(testValue, model.getTu(), "getTu should return the set value"); + } + + @Test + void testSetGetString() { + String testValue = "Hello World"; + model.setStr(testValue); + assertEquals(testValue, model.getStr(), "getStr should return the set value"); + } + + @Test + void testSetGetBigInteger() { + BigInteger testValue = new BigInteger("123456789012345678901234567890"); + model.setBi(testValue); + assertEquals(testValue, model.getBi(), "getBi should return the set value"); + } + + @Test + void testSetGetBigDecimal() { + BigDecimal testValue = new BigDecimal("1234567890.12345678901234567890"); + model.setBd(testValue); + assertEquals(testValue, model.getBd(), "getBd should return the set value"); + } + + @Test + void testInheritanceFromParent() { + // Verify that the model inherits from Parent class + assertTrue(model instanceof Parent, "Model should extend Parent class"); + } + + @Test + void testMultipleValueChanges() { + // Test changing values multiple times + model.setF(1.0f); + assertEquals(1.0f, model.getF(), "f should be 1.0f"); + + model.setF(2.0f); + assertEquals(2.0f, model.getF(), "f should be 2.0f after second assignment"); + + model.setD(10.0d); + assertEquals(10.0d, model.getD(), "d should be 10.0d"); + + model.setD(20.0d); + assertEquals(20.0d, model.getD(), "d should be 20.0d after second assignment"); + } + + @Test + void testNullHandling() { + // Test setting object fields to null + model.setTu(null); + model.setStr(null); + model.setBi(null); + model.setBd(null); + + assertNull(model.getTu(), "tu should be null after setting to null"); + assertNull(model.getStr(), "str should be null after setting to null"); + assertNull(model.getBi(), "bi should be null after setting to null"); + assertNull(model.getBd(), "bd should be null after setting to null"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/ParentTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/ParentTest.java new file mode 100644 index 000000000..30d489e6e --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/ParentTest.java @@ -0,0 +1,86 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the Parent class + */ +class ParentTest { + + private Parent parent; + + @BeforeEach + void setUp() { + parent = new Parent(); + } + + @Test + void testDefaultValues() { + // Verify all primitive fields have their default values + assertEquals((byte) 0, parent.getB(), "byte field b should be 0 by default"); + assertEquals((short) 0, parent.getS(), "short field s should be 0 by default"); + assertEquals(0, parent.getI(), "int field i should be 0 by default"); + assertEquals(0L, parent.getL(), "long field l should be 0L by default"); + } + + @Test + void testSetGetByte() { + byte testValue = (byte) 42; + parent.setB(testValue); + assertEquals(testValue, parent.getB(), "getB should return the set value"); + } + + @Test + void testSetGetShort() { + short testValue = (short) 1000; + parent.setS(testValue); + assertEquals(testValue, parent.getS(), "getS should return the set value"); + } + + @Test + void testSetGetInt() { + int testValue = 123456; + parent.setI(testValue); + assertEquals(testValue, parent.getI(), "getI should return the set value"); + } + + @Test + void testSetGetLong() { + long testValue = 9876543210L; + parent.setL(testValue); + assertEquals(testValue, parent.getL(), "getL should return the set value"); + } + + @Test + void testInheritanceFromAncestor() { + // Verify that the parent inherits from Ancestor class + assertTrue(parent instanceof Ancestor, "Parent should extend Ancestor class"); + + // Test inherited functionality + assertFalse(parent.isZ(), "Should inherit default z value of false from Ancestor"); + + parent.setZ(true); + assertTrue(parent.isZ(), "Should be able to modify inherited z field"); + } + + @Test + void testMultipleValueChanges() { + // Test changing values multiple times + parent.setB((byte) 1); + assertEquals((byte) 1, parent.getB(), "b should be 1"); + + parent.setB((byte) 2); + assertEquals((byte) 2, parent.getB(), "b should be 2 after second assignment"); + + parent.setI(100); + assertEquals(100, parent.getI(), "i should be 100"); + + parent.setI(200); + assertEquals(200, parent.getI(), "i should be 200 after second assignment"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/PrimitiveTypeModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/PrimitiveTypeModelTest.java new file mode 100644 index 000000000..1f28bcb4d --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/PrimitiveTypeModelTest.java @@ -0,0 +1,81 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +/** + * Unit tests for the PrimitiveTypeModel class + */ +class PrimitiveTypeModelTest { + + private PrimitiveTypeModel model; + + @BeforeEach + void setUp() { + model = new PrimitiveTypeModel(); + } + + @Test + void testDefaultValues() { + // Verify all primitive fields have their default values + assertFalse(model.isZ(), "boolean field z should be false by default"); + assertEquals((byte) 0, model.getB(), "byte field b should be 0 by default"); + assertEquals('\u0000', model.getC(), "char field c should be null character by default"); + assertEquals((short) 0, model.getS(), "short field s should be 0 by default"); + assertEquals(0, model.getI(), "int field i should be 0 by default"); + assertEquals(0L, model.getL(), "long field l should be 0L by default"); + assertEquals(0.0f, model.getF(), "float field f should be 0.0f by default"); + assertEquals(0.0d, model.getD(), "double field d should be 0.0d by default"); + } + + @Test + void testBooleanField() { + // Test boolean field specifically since it uses 'is' prefix instead of 'get' + assertFalse(model.isZ(), "Initial value should be false"); + } + + @Test + void testPrimitiveTypesRange() { + // Test various ranges for different primitive types + PrimitiveTypeModel testModel = new PrimitiveTypeModel(); + + // Boolean + // Cannot set values directly as there are no setters, but we can verify the default + + // Byte range (-128 to 127) + byte minByte = Byte.MIN_VALUE; + byte maxByte = Byte.MAX_VALUE; + + // Char range (0 to 65535) + char minChar = Character.MIN_VALUE; + char maxChar = Character.MAX_VALUE; + + // Short range (-32768 to 32767) + short minShort = Short.MIN_VALUE; + short maxShort = Short.MAX_VALUE; + + // Int range + int minInt = Integer.MIN_VALUE; + int maxInt = Integer.MAX_VALUE; + + // Long range + long minLong = Long.MIN_VALUE; + long maxLong = Long.MAX_VALUE; + + // Float range + float minFloat = Float.MIN_VALUE; + float maxFloat = Float.MAX_VALUE; + + // Double range + double minDouble = Double.MIN_VALUE; + double maxDouble = Double.MAX_VALUE; + + // These are just to ensure the getters work with different possible values + // Since there are no setters, we're just validating the getters return values + assertEquals(0, testModel.getI(), "Integer field should have default value"); + assertEquals(0.0f, testModel.getF(), "Float field should have default value"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/SimpleTypeModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/SimpleTypeModelTest.java new file mode 100644 index 000000000..6a0d24251 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/SimpleTypeModelTest.java @@ -0,0 +1,182 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * Unit tests for the SimpleTypeModel class + */ +class SimpleTypeModelTest { + + private SimpleTypeModel model; + + @BeforeEach + void setUp() { + model = new SimpleTypeModel(); + } + + @Test + void testDefaultValues() { + // Verify all object fields are null by default + assertNull(model.getV(), "Void field v should be null by default"); + assertNull(model.getZ(), "Boolean field z should be null by default"); + assertNull(model.getC(), "Character field c should be null by default"); + assertNull(model.getB(), "Byte field b should be null by default"); + assertNull(model.getS(), "Short field s should be null by default"); + assertNull(model.getI(), "Integer field i should be null by default"); + assertNull(model.getL(), "Long field l should be null by default"); + assertNull(model.getF(), "Float field f should be null by default"); + assertNull(model.getD(), "Double field d should be null by default"); + assertNull(model.getStr(), "String field str should be null by default"); + assertNull(model.getBd(), "BigDecimal field bd should be null by default"); + assertNull(model.getBi(), "BigInteger field bi should be null by default"); + assertNull(model.getDt(), "Date field dt should be null by default"); + } + + @Test + void testSetGetVoid() { + // Void is always null, so setting it to null should work + model.setV(null); + assertNull(model.getV(), "Void should always be null"); + } + + @Test + void testSetGetBoolean() { + Boolean testValue = Boolean.TRUE; + model.setZ(testValue); + assertEquals(testValue, model.getZ(), "getZ should return the set value"); + + model.setZ(Boolean.FALSE); + assertEquals(Boolean.FALSE, model.getZ(), "getZ should return the set value"); + } + + @Test + void testSetGetCharacter() { + Character testValue = 'A'; + model.setC(testValue); + assertEquals(testValue, model.getC(), "getC should return the set value"); + } + + @Test + void testSetGetByte() { + Byte testValue = (byte) 42; + model.setB(testValue); + assertEquals(testValue, model.getB(), "getB should return the set value"); + } + + @Test + void testSetGetShort() { + Short testValue = (short) 1000; + model.setS(testValue); + assertEquals(testValue, model.getS(), "getS should return the set value"); + } + + @Test + void testSetGetInteger() { + Integer testValue = 123456; + model.setI(testValue); + assertEquals(testValue, model.getI(), "getI should return the set value"); + } + + @Test + void testSetGetLong() { + Long testValue = 9876543210L; + model.setL(testValue); + assertEquals(testValue, model.getL(), "getL should return the set value"); + } + + @Test + void testSetGetFloat() { + Float testValue = 3.14f; + model.setF(testValue); + assertEquals(testValue, model.getF(), "getF should return the set value"); + } + + @Test + void testSetGetDouble() { + Double testValue = 2.71828d; + model.setD(testValue); + assertEquals(testValue, model.getD(), "getD should return the set value"); + } + + @Test + void testSetGetString() { + String testValue = "Hello World"; + model.setStr(testValue); + assertEquals(testValue, model.getStr(), "getStr should return the set value"); + } + + @Test + void testSetGetBigDecimal() { + BigDecimal testValue = new BigDecimal("1234567890.12345678901234567890"); + model.setBd(testValue); + assertEquals(testValue, model.getBd(), "getBd should return the set value"); + } + + @Test + void testSetGetBigInteger() { + BigInteger testValue = new BigInteger("123456789012345678901234567890"); + model.setBi(testValue); + assertEquals(testValue, model.getBi(), "getBi should return the set value"); + } + + @Test + void testSetGetDate() { + Date testValue = new Date(); + model.setDt(testValue); + assertEquals(testValue, model.getDt(), "getDt should return the set value"); + } + + @Test + void testNullHandling() { + // Test setting all fields to null + model.setZ(null); + model.setC(null); + model.setB(null); + model.setS(null); + model.setI(null); + model.setL(null); + model.setF(null); + model.setD(null); + model.setStr(null); + model.setBd(null); + model.setBi(null); + model.setDt(null); + + assertNull(model.getZ(), "z should be null after setting to null"); + assertNull(model.getC(), "c should be null after setting to null"); + assertNull(model.getB(), "b should be null after setting to null"); + assertNull(model.getS(), "s should be null after setting to null"); + assertNull(model.getI(), "i should be null after setting to null"); + assertNull(model.getL(), "l should be null after setting to null"); + assertNull(model.getF(), "f should be null after setting to null"); + assertNull(model.getD(), "d should be null after setting to null"); + assertNull(model.getStr(), "str should be null after setting to null"); + assertNull(model.getBd(), "bd should be null after setting to null"); + assertNull(model.getBi(), "bi should be null after setting to null"); + assertNull(model.getDt(), "dt should be null after setting to null"); + } + + @Test + void testMultipleValueChanges() { + // Test changing values multiple times + model.setI(100); + assertEquals(Integer.valueOf(100), model.getI(), "i should be 100"); + + model.setI(200); + assertEquals(Integer.valueOf(200), model.getI(), "i should be 200 after second assignment"); + + model.setStr("first"); + assertEquals("first", model.getStr(), "str should be 'first'"); + + model.setStr("second"); + assertEquals("second", model.getStr(), "str should be 'second' after second assignment"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/StringArrayListTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/StringArrayListTest.java new file mode 100644 index 000000000..cc417fa20 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/StringArrayListTest.java @@ -0,0 +1,137 @@ +package io.microsphere.test.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the StringArrayList class + */ +class StringArrayListTest { + + private StringArrayList stringArrayList; + + @BeforeEach + void setUp() { + stringArrayList = new StringArrayList(); + } + + @Test + void testDefaultConstructor() { + // Verify that the StringArrayList can be instantiated and is empty initially + assertNotNull(stringArrayList, "StringArrayList should be instantiated successfully"); + assertTrue(stringArrayList.isEmpty(), "StringArrayList should be empty by default"); + assertEquals(0, stringArrayList.size(), "StringArrayList size should be 0 by default"); + } + + @Test + void testAddAndGetSize() { + // Test adding elements and checking size + stringArrayList.add("element1"); + assertEquals(1, stringArrayList.size(), "Size should be 1 after adding one element"); + + stringArrayList.add("element2"); + assertEquals(2, stringArrayList.size(), "Size should be 2 after adding two elements"); + } + + @Test + void testAddAndGetElements() { + // Test adding elements and retrieving them + String element1 = "Hello"; + String element2 = "World"; + String element3 = "Test"; + + stringArrayList.add(element1); + stringArrayList.add(element2); + stringArrayList.add(element3); + + assertEquals(element1, stringArrayList.get(0), "Element at index 0 should match added value"); + assertEquals(element2, stringArrayList.get(1), "Element at index 1 should match added value"); + assertEquals(element3, stringArrayList.get(2), "Element at index 2 should match added value"); + } + + @Test + void testAddAll() { + // Test adding multiple elements at once + String[] elements = {"item1", "item2", "item3"}; + + boolean result = stringArrayList.addAll(Arrays.asList(elements)); + assertTrue(result, "addAll should return true when elements are added"); + + assertEquals(3, stringArrayList.size(), "Size should match number of added elements"); + assertEquals("item1", stringArrayList.get(0), "First element should match"); + assertEquals("item2", stringArrayList.get(1), "Second element should match"); + assertEquals("item3", stringArrayList.get(2), "Third element should match"); + } + + @Test + void testRemoveElement() { + // Test removing elements + stringArrayList.add("element1"); + stringArrayList.add("element2"); + + boolean removed = stringArrayList.remove("element1"); + assertTrue(removed, "Should return true when element is successfully removed"); + assertEquals(1, stringArrayList.size(), "Size should decrease after removal"); + assertEquals("element2", stringArrayList.get(0), "Remaining element should still be accessible"); + } + + @Test + void testClear() { + // Test clearing all elements + stringArrayList.add("element1"); + stringArrayList.add("element2"); + stringArrayList.add("element3"); + + assertEquals(3, stringArrayList.size(), "Size should be 3 before clear"); + + stringArrayList.clear(); + + assertTrue(stringArrayList.isEmpty(), "List should be empty after clear"); + assertEquals(0, stringArrayList.size(), "Size should be 0 after clear"); + } + + @Test + void testContains() { + // Test contains functionality + String element = "test_element"; + stringArrayList.add(element); + + assertTrue(stringArrayList.contains(element), "List should contain the added element"); + assertFalse(stringArrayList.contains("non_existent"), "List should not contain non-existent element"); + } + + @Test + void testIterator() { + // Test iterator functionality + String[] elements = {"a", "b", "c"}; + stringArrayList.addAll(Arrays.asList(elements)); + + Iterator iterator = stringArrayList.iterator(); + int index = 0; + + while (iterator.hasNext() && index < elements.length) { + String nextElement = iterator.next(); + assertEquals(elements[index], nextElement, "Iterator should return elements in order"); + index++; + } + + assertFalse(iterator.hasNext(), "Iterator should be exhausted after processing all elements"); + } + + @Test + void testInheritanceFromArrayList() { + // Verify that StringArrayList properly extends ArrayList + assertTrue(stringArrayList instanceof ArrayList, "StringArrayList should extend ArrayList"); + assertTrue(stringArrayList instanceof java.util.List, "StringArrayList should implement List interface"); + assertTrue(stringArrayList instanceof java.util.Collection, "StringArrayList should implement Collection interface"); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/service/DefaultTestServiceTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/service/DefaultTestServiceTest.java new file mode 100644 index 000000000..a7713e79a --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/service/DefaultTestServiceTest.java @@ -0,0 +1,83 @@ +package io.microsphere.test.service; + +import io.microsphere.test.model.Model; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * Unit tests for the DefaultTestService class. + */ +class DefaultTestServiceTest { + + private DefaultTestService defaultTestService; + + @BeforeEach + void setUp() { + defaultTestService = new DefaultTestService(); + } + + /** + * Test the echo method. + * Verifies that the method correctly prefixes the input message with "[ECHO] ". + */ + @Test + void testEcho() { + String input = "Hello, World!"; + String expectedOutput = "[ECHO] Hello, World!"; + String actualOutput = defaultTestService.echo(input); + assertEquals(expectedOutput, actualOutput, "The echo method should prefix the message with '[ECHO] '."); + } + + /** + * Test the model method. + * Verifies that the method returns the same Model object that was passed in. + */ + @Test + void testModel() { + Model inputModel = new Model(); + Model outputModel = defaultTestService.model(inputModel); + assertSame(inputModel, outputModel, "The model method should return the same Model object."); + } + + /** + * Test the testPrimitive method. + * Currently, this method returns null, so the test verifies that null is returned. + */ + @Test + void testTestPrimitive() { + boolean z = true; + int i = 42; + String result = defaultTestService.testPrimitive(z, i); + assertNull(result, "The testPrimitive method should return null."); + } + + /** + * Test the testEnum method. + * Currently, this method returns null, so the test verifies that null is returned. + */ + @Test + void testTestEnum() { + TimeUnit timeUnit = TimeUnit.SECONDS; + Model result = defaultTestService.testEnum(timeUnit); + assertNull(result, "The testEnum method should return null."); + } + + /** + * Test the testArray method. + * Currently, this method returns null, so the test verifies that null is returned. + */ + @Test + void testTestArray() { + String[] strArray = {"a", "b", "c"}; + int[] intArray = {1, 2, 3}; + Model[] modelArray = {new Model(), new Model()}; + String result = defaultTestService.testArray(strArray, intArray, modelArray); + assertNull(result, "The testArray method should return null."); + } +} diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/service/GenericTestServiceTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/service/GenericTestServiceTest.java new file mode 100644 index 000000000..d97ca6011 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/service/GenericTestServiceTest.java @@ -0,0 +1,58 @@ +package io.microsphere.test.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the GenericTestService class. + */ +class GenericTestServiceTest { + + private GenericTestService genericTestService; + + @BeforeEach + void setUp() { + genericTestService = new GenericTestService(); + } + + /** + * Test the echo method. + * Verifies that the method correctly prefixes the input message with "[ECHO] ". + */ + @Test + void testEcho() { + String input = "Hello, World!"; + String expectedOutput = "[ECHO] Hello, World!"; + String actualOutput = genericTestService.echo(input); + assertEquals(expectedOutput, actualOutput, "The echo method should prefix the message with '[ECHO] '."); + } + + /** + * Test inheritance from DefaultTestService. + * Verifies that methods from the parent class are accessible and functional. + */ + @Test + void testInheritedMethods() { + // Example: Testing a method inherited from DefaultTestService + // Assuming DefaultTestService has a method like model(Model model) + // Uncomment and adapt the following lines if such a method exists: + /* + Model inputModel = new Model(); + Model outputModel = genericTestService.model(inputModel); + assertSame(inputModel, outputModel, "The inherited model method should return the same Model object."); + */ + } + + /** + * Test implementation of EventListener interface. + * Verifies that the class correctly implements the EventListener marker interface. + */ + @Test + void testEventListenerImplementation() { + assertTrue(genericTestService instanceof java.util.EventListener, + "GenericTestService should implement the EventListener interface."); + } +} diff --git a/microsphere-java-test/src/test/resources/META-INF/services/javax.annotation.processing.Processor b/microsphere-java-test/src/test/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 000000000..6b85e96b8 --- /dev/null +++ b/microsphere-java-test/src/test/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +io.microsphere.test.annotation.processing.TestProcessor \ No newline at end of file From 54ce8d03d0dded837d35285c333e5fb80f23542f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 17:44:35 +0800 Subject: [PATCH 014/438] Use Invocation API for annotation processing tests Refactor annotation-processing test harness to use JUnit 5 reflective invocation API. AbstractAnnotationProcessingTest: remove ThreadLocal holder and @BeforeEach/@AfterEach, change beforeTest/afterTest signatures to accept ReflectiveInvocationContext and ExtensionContext (afterTest also receives result and failure), add necessary imports and reflection types. AnnotationProcessingTestProcessor: make package-private, use Invocation type directly, call beforeTest/afterTest with contexts and pass result/failure, and populate processingEnv, Elements/Types and test type information during prepare. CompilerInvocationInterceptor: make package-private, obtain test instance from invocationContext target, use the test class loader, and load SPI Processor implementations via ServiceLoader (merging them with the test processor). Also cleanup imports/usages in test class (remove assertion referencing removed holder). Overall adjusts lifecycle integration so the annotation processor drives the test invocation and reporting. --- .../AbstractAnnotationProcessingTest.java | 31 +++---------- .../AnnotationProcessingTestProcessor.java | 44 ++++++++++++++----- .../CompilerInvocationInterceptor.java | 15 ++++--- .../processing/AnnotationProcessingTest.java | 2 - 4 files changed, 50 insertions(+), 42 deletions(-) diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java index 2b1bcd330..5a4d5d826 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java @@ -16,10 +16,10 @@ */ package io.microsphere.test.annotation.processing; -import io.microsphere.test.service.TestServiceImpl; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import io.microsphere.annotation.Nullable; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; @@ -36,6 +36,7 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Collection; import java.util.List; @@ -105,8 +106,6 @@ public abstract class AbstractAnnotationProcessingTest { protected static final AnnotationMirror NULL_ANNOTATION_MIRROR = null; - static ThreadLocal testInstanceHolder = new ThreadLocal<>(); - protected RoundEnvironment roundEnv; protected ProcessingEnvironment processingEnv; @@ -125,29 +124,13 @@ public abstract class AbstractAnnotationProcessingTest { protected DeclaredType testDeclaredType; - @BeforeEach - final void setUp() { - testInstanceHolder.set(this); - } - - @AfterEach - final void tearDown() { - testInstanceHolder.remove(); - } - protected void addCompiledClasses(Set> compiledClasses) { } - protected void beforeTest() { - this.testClass = TestServiceImpl.class; - this.testClassName = TestServiceImpl.class.getName(); - this.elements = processingEnv.getElementUtils(); - this.testTypeElement = this.elements.getTypeElement(this.testClassName); - this.testTypeMirror = this.testTypeElement.asType(); - this.testDeclaredType = (DeclaredType) testTypeElement.asType(); + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { } - protected void afterTest() { + protected void afterTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext, + @Nullable Object result, @Nullable Throwable failure) { } - } \ No newline at end of file diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java index 51511cc5c..25b359469 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java @@ -17,14 +17,19 @@ package io.microsphere.test.annotation.processing; import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.InvocationInterceptor; +import org.junit.jupiter.api.extension.InvocationInterceptor.Invocation; import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; import java.lang.reflect.Method; import java.util.Set; @@ -38,17 +43,17 @@ * @since 1.0.0 */ @SupportedAnnotationTypes(WILDCARD) -public class AnnotationProcessingTestProcessor extends AbstractProcessor { +class AnnotationProcessingTestProcessor extends AbstractProcessor { private final AbstractAnnotationProcessingTest abstractAnnotationProcessingTest; - private final InvocationInterceptor.Invocation invocation; + private final Invocation invocation; private final ReflectiveInvocationContext invocationContext; private final ExtensionContext extensionContext; - public AnnotationProcessingTestProcessor(AbstractAnnotationProcessingTest abstractAnnotationProcessingTest, InvocationInterceptor.Invocation invocation, + public AnnotationProcessingTestProcessor(AbstractAnnotationProcessingTest abstractAnnotationProcessingTest, Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { this.abstractAnnotationProcessingTest = abstractAnnotationProcessingTest; @@ -59,25 +64,42 @@ public AnnotationProcessingTestProcessor(AbstractAnnotationProcessingTest abstra @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { + ReflectiveInvocationContext invocationContext = this.invocationContext; + ExtensionContext extensionContext = this.extensionContext; if (!roundEnv.processingOver()) { prepare(roundEnv); - abstractAnnotationProcessingTest.beforeTest(); + Object result = null; + Throwable failure = null; + abstractAnnotationProcessingTest.beforeTest(invocationContext, extensionContext); try { - invocation.proceed(); + result = invocation.proceed(); } catch (Throwable throwable) { - throw new RuntimeException(throwable); + failure = throwable; } finally { - abstractAnnotationProcessingTest.afterTest(); + abstractAnnotationProcessingTest.afterTest(invocationContext, extensionContext, result, failure); } } return false; } protected void prepare(RoundEnvironment roundEnv) { + ProcessingEnvironment processingEnv = super.processingEnv; + Elements elements = processingEnv.getElementUtils(); + Types types = processingEnv.getTypeUtils(); + Class testClass = this.invocationContext.getTargetClass(); + String testClassName = testClass.getName(); + TypeElement testTypeElement = elements.getTypeElement(testClassName); + TypeMirror testType = testTypeElement.asType(); + abstractAnnotationProcessingTest.roundEnv = roundEnv; - abstractAnnotationProcessingTest.processingEnv = super.processingEnv; - abstractAnnotationProcessingTest.elements = super.processingEnv.getElementUtils(); - abstractAnnotationProcessingTest.types = super.processingEnv.getTypeUtils(); + abstractAnnotationProcessingTest.processingEnv = processingEnv; + abstractAnnotationProcessingTest.elements = elements; + abstractAnnotationProcessingTest.types = types; + abstractAnnotationProcessingTest.testClass = testClass; + abstractAnnotationProcessingTest.testClassName = testClassName; + abstractAnnotationProcessingTest.testTypeElement = testTypeElement; + abstractAnnotationProcessingTest.testTypeMirror = testType; + abstractAnnotationProcessingTest.testDeclaredType = (DeclaredType) testType; } @Override diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java index 296ca9ac3..1d0b0518c 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java @@ -26,10 +26,10 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.ServiceLoader; import java.util.Set; -import static io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest.testInstanceHolder; -import static io.microsphere.util.ServiceLoaderUtils.loadServicesList; +import static java.util.ServiceLoader.load; /** @@ -38,21 +38,26 @@ * @author Mercy * @since 1.0.0 */ -public class CompilerInvocationInterceptor implements InvocationInterceptor { +class CompilerInvocationInterceptor implements InvocationInterceptor { @Override public void interceptTestMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { Set> compiledClasses = new LinkedHashSet<>(); - AbstractAnnotationProcessingTest abstractAnnotationProcessingTest = testInstanceHolder.get(); + AbstractAnnotationProcessingTest abstractAnnotationProcessingTest = (AbstractAnnotationProcessingTest) invocationContext.getTarget().get(); Class testClass = extensionContext.getTestClass().get(); + ClassLoader classLoader = testClass.getClassLoader(); compiledClasses.add(testClass); abstractAnnotationProcessingTest.addCompiledClasses(compiledClasses); Compiler compiler = new Compiler(); compiler.sourcePaths(testClass); - List processors = new LinkedList<>(loadServicesList(Processor.class, testClass.getClassLoader())); + + List processors = new LinkedList<>(); processors.add(new AnnotationProcessingTestProcessor(abstractAnnotationProcessingTest, invocation, invocationContext, extensionContext)); + // Loads the SPI instances of Processor + ServiceLoader loadedProcessors = load(Processor.class, classLoader); + loadedProcessors.forEach(processors::add); compiler.processors(processors.toArray(new Processor[0])); compiler.compile(compiledClasses.toArray(new Class[0])); } diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java index 935b1e5f5..51e158304 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java @@ -25,7 +25,6 @@ import java.util.Collection; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; /** @@ -66,6 +65,5 @@ void test() { assertNull(NULL_METHOD); assertNull(NULL_METHOD_ARRAY); assertNull(NULL_ANNOTATION_MIRROR); - assertNotNull(testInstanceHolder); } } From 8f0905632acb5f58e686998e9ab7169c82ea7917 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 17:48:59 +0800 Subject: [PATCH 015/438] Add TestServiceImpl tests; adjust annotation processor Add a new JUnit test class TestServiceImplTest (Mockito-based) to validate TestServiceImpl behavior (echo, close, constructors and DI). Make small API/encapsulation adjustments to the annotation-processing test infra: add Javadoc/comments for addCompiledClasses, beforeTest and afterTest in AbstractAnnotationProcessingTest; reduce visibility of AnnotationProcessingTestProcessor constructor and prepare method to package-private; and rename local variables in CompilerInvocationInterceptor to improve clarity and pass the test instance into the processor. These changes improve test coverage and tighten processor APIs. --- .../AbstractAnnotationProcessingTest.java | 19 +++++ .../AnnotationProcessingTestProcessor.java | 8 +- .../CompilerInvocationInterceptor.java | 9 +-- .../test/service/TestServiceImplTest.java | 75 +++++++++++++++++++ 4 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/service/TestServiceImplTest.java diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java index 5a4d5d826..0979eab68 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java @@ -124,12 +124,31 @@ public abstract class AbstractAnnotationProcessingTest { protected DeclaredType testDeclaredType; + /** + * The classes to be compiled. + * + * @param compiledClasses the mutable {@link Set} for classes to be compiled + */ protected void addCompiledClasses(Set> compiledClasses) { } + /** + * Before Test + * + * @param invocationContext {@link ReflectiveInvocationContext} + * @param extensionContext {@link ExtensionContext} + */ protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { } + /** + * After Test + * + * @param invocationContext {@link ReflectiveInvocationContext} + * @param extensionContext {@link ExtensionContext} + * @param result the result after test method returning + * @param failure the failure after the test methods' execution + */ protected void afterTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext, @Nullable Object result, @Nullable Throwable failure) { } diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java index 25b359469..abc23576e 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java @@ -53,9 +53,9 @@ class AnnotationProcessingTestProcessor extends AbstractProcessor { private final ExtensionContext extensionContext; - public AnnotationProcessingTestProcessor(AbstractAnnotationProcessingTest abstractAnnotationProcessingTest, Invocation invocation, - ReflectiveInvocationContext invocationContext, - ExtensionContext extensionContext) { + AnnotationProcessingTestProcessor(AbstractAnnotationProcessingTest abstractAnnotationProcessingTest, Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { this.abstractAnnotationProcessingTest = abstractAnnotationProcessingTest; this.invocation = invocation; this.invocationContext = invocationContext; @@ -82,7 +82,7 @@ public boolean process(Set annotations, RoundEnvironment return false; } - protected void prepare(RoundEnvironment roundEnv) { + void prepare(RoundEnvironment roundEnv) { ProcessingEnvironment processingEnv = super.processingEnv; Elements elements = processingEnv.getElementUtils(); Types types = processingEnv.getTypeUtils(); diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java index 1d0b0518c..a0d113350 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java @@ -41,20 +41,19 @@ class CompilerInvocationInterceptor implements InvocationInterceptor { @Override - public void interceptTestMethod(Invocation invocation, - ReflectiveInvocationContext invocationContext, + public void interceptTestMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { Set> compiledClasses = new LinkedHashSet<>(); - AbstractAnnotationProcessingTest abstractAnnotationProcessingTest = (AbstractAnnotationProcessingTest) invocationContext.getTarget().get(); + AbstractAnnotationProcessingTest test = (AbstractAnnotationProcessingTest) invocationContext.getTarget().get(); Class testClass = extensionContext.getTestClass().get(); ClassLoader classLoader = testClass.getClassLoader(); compiledClasses.add(testClass); - abstractAnnotationProcessingTest.addCompiledClasses(compiledClasses); + test.addCompiledClasses(compiledClasses); Compiler compiler = new Compiler(); compiler.sourcePaths(testClass); List processors = new LinkedList<>(); - processors.add(new AnnotationProcessingTestProcessor(abstractAnnotationProcessingTest, invocation, invocationContext, extensionContext)); + processors.add(new AnnotationProcessingTestProcessor(test, invocation, invocationContext, extensionContext)); // Loads the SPI instances of Processor ServiceLoader loadedProcessors = load(Processor.class, classLoader); loadedProcessors.forEach(processors::add); diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/service/TestServiceImplTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/service/TestServiceImplTest.java new file mode 100644 index 000000000..272aba056 --- /dev/null +++ b/microsphere-java-test/src/test/java/io/microsphere/test/service/TestServiceImplTest.java @@ -0,0 +1,75 @@ +package io.microsphere.test.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.*; + +/** + * Unit tests for the TestServiceImpl class. + */ +class TestServiceImplTest { + + private TestServiceImpl testService; + private ApplicationContext mockContext; + private Environment mockEnvironment; + + @BeforeEach + void setUp() { + // Mock dependencies + mockContext = mock(ApplicationContext.class); + mockEnvironment = mock(Environment.class); + + // Initialize the service with mocked dependencies + testService = new TestServiceImpl(mockEnvironment); + testService.context = mockContext; // Inject mocked ApplicationContext + } + + /** + * Test the echo method. + * Verifies that the method correctly prefixes the input message with "[ECHO] ". + */ + @Test + void testEcho() { + String input = "Hello, World!"; + String expectedOutput = "[ECHO] Hello, World!"; + String actualOutput = testService.echo(input); + assertEquals(expectedOutput, actualOutput, "The echo method should prefix the message with '[ECHO] '."); + } + + /** + * Test the close method. + * Verifies that the method does not throw any exceptions. + */ + @Test + void testClose() { + assertDoesNotThrow(() -> testService.close(), "The close method should not throw any exceptions."); + } + + /** + * Test constructor with Environment parameter. + * Verifies that the environment is properly injected. + */ + @Test + void testConstructorWithEnvironment() { + assertNotNull(testService.environment, "The environment should be injected via constructor."); + assertSame(mockEnvironment, testService.environment, "The injected environment should match the mocked instance."); + } + + /** + * Test default constructor. + * Verifies that the service can be instantiated without an Environment. + */ + @Test + void testDefaultConstructor() { + TestServiceImpl service = new TestServiceImpl(); + assertNull(service.environment, "The environment should be null when using the default constructor."); + } +} From 658ea193b164a99fd477dfb0c1abf4b05349ce87 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 17:49:15 +0800 Subject: [PATCH 016/438] Add ThrowableUtils utility class Introduce io.microsphere.util.ThrowableUtils: a new utility class (with ASF license header and author info) providing getRootCause(Throwable) to traverse and return the deepest cause in a throwable chain. The class implements Utils and has a private constructor to prevent instantiation. --- .../io/microsphere/util/ThrowableUtils.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/util/ThrowableUtils.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ThrowableUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ThrowableUtils.java new file mode 100644 index 000000000..8f6d32c23 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ThrowableUtils.java @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package io.microsphere.util; + +/** + * The uitlities class for {@link Throwable} + * + * @author Mercy + * @see Throwable + * @since 1.0.0 + */ +public abstract class ThrowableUtils implements Utils { + + public static Throwable getRootCause(Throwable throwable) { + Throwable rootCause = throwable; + while (rootCause.getCause() != null) { + rootCause = rootCause.getCause(); + } + return rootCause; + } + + + private ThrowableUtils() { + } +} From 866143aa7d3c88b2a2bb86a7105b1ef19ce9d4f8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 17:49:29 +0800 Subject: [PATCH 017/438] Add ThrowableUtils unit tests Add JUnit 5 tests for ThrowableUtils#getRootCause. The new test file verifies retrieving the root cause from a nested Throwable, checks behavior when using ThrowableAction.execute to handle thrown exceptions, and ensures rethrowing the root cause yields the expected NullPointerException. --- .../microsphere/util/ThrowableUtilsTest.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/util/ThrowableUtilsTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ThrowableUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ThrowableUtilsTest.java new file mode 100644 index 000000000..883f8e5e1 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ThrowableUtilsTest.java @@ -0,0 +1,64 @@ +/* + * 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. + */ + +package io.microsphere.util; + + +import io.microsphere.lang.function.ThrowableAction; +import org.junit.jupiter.api.Test; + +import static io.microsphere.lang.function.ThrowableAction.execute; +import static io.microsphere.util.ThrowableUtils.getRootCause; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * {@link ThrowableUtils} Test + * + * @author Mercy + * @see ThrowableUtils + * @since 1.0.0 + */ +class ThrowableUtilsTest { + + @Test + void testGetRootCause() { + Throwable rootCause = new Throwable("Root Cause"); + Throwable throwable = new Throwable("Throwable", rootCause); + assertSame(rootCause, getRootCause(throwable)); + + ThrowableAction action = () -> { + throw throwable; + }; + + execute(action, e -> { + assertSame(rootCause, getRootCause(e)); + }); + + assertThrows(NullPointerException.class, () -> { + try { + ThrowableAction a = () -> { + String s = null; + s.toString(); + }; + a.execute(); + } catch (Throwable e) { + throw getRootCause(e); + } + }); + } +} \ No newline at end of file From 86253ae58bbd99a192c0c44861b7e116a7cf4b2b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 18:25:10 +0800 Subject: [PATCH 018/438] Add test and afterTest failure assertion Introduce a failing test and verify test lifecycle failure handling. Adds testOnFailure() which throws a RuntimeException("For testing") and overrides afterTest(ReflectiveInvocationContext, ExtensionContext, Object, Throwable) to assert the failure message when a failure is present. Also adds the required imports and an assertEquals assertion. --- .../processing/AnnotationProcessingTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java index 51e158304..287fb77f9 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java @@ -18,13 +18,17 @@ package io.microsphere.test.annotation.processing; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import javax.lang.model.element.Element; import javax.lang.model.type.TypeMirror; +import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Collection; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; /** @@ -66,4 +70,17 @@ void test() { assertNull(NULL_METHOD_ARRAY); assertNull(NULL_ANNOTATION_MIRROR); } + + @Test + void testOnFailure() { + throw new RuntimeException("For testing"); + } + + @Override + protected void afterTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext, Object result, Throwable failure) { + super.afterTest(invocationContext, extensionContext, result, failure); + if (failure != null) { + assertEquals("For testing", failure.getMessage()); + } + } } From ade92cf79cd4b8a682adb565bdddabcb82e6bd1b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:02:02 +0800 Subject: [PATCH 019/438] Add test deps; adapt annotation processing tests Add Microsphere test libraries to the module POM (microsphere-lang-model, microsphere-jdk-tools (test scope), and microsphere-java-test (test scope)). Update unit tests to extend AbstractAnnotationProcessingTest and adjust lifecycle hooks: beforeTest/afterTest signatures now accept ReflectiveInvocationContext and ExtensionContext (and afterTest includes result/failure). Import necessary classes (ReflectiveInvocationContext, ExtensionContext, Method) and update test setup/teardown to use the new testing extension API. --- microsphere-annotation-processor/pom.xml | 23 +++++++++++++++++++ .../processor/FilerProcessorTest.java | 7 +++++- .../processor/ResourceProcessorTest.java | 8 +++++-- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/microsphere-annotation-processor/pom.xml b/microsphere-annotation-processor/pom.xml index 0af57d196..e5558b6fe 100644 --- a/microsphere-annotation-processor/pom.xml +++ b/microsphere-annotation-processor/pom.xml @@ -32,6 +32,13 @@ ${revision} + + + io.github.microsphere-projects + microsphere-lang-model + ${revision} + + org.junit.jupiter @@ -45,6 +52,22 @@ test + + + io.github.microsphere-projects + microsphere-jdk-tools + ${revision} + test + + + + + io.github.microsphere-projects + microsphere-java-test + ${revision} + test + + ch.qos.logback diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/FilerProcessorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/FilerProcessorTest.java index 47588edde..132685903 100644 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/FilerProcessorTest.java +++ b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/FilerProcessorTest.java @@ -18,11 +18,16 @@ package io.microsphere.annotation.processor; +import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; +import java.lang.reflect.Method; + import static io.microsphere.annotation.processor.ResourceProcessor.exists; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -40,7 +45,7 @@ class FilerProcessorTest extends AbstractAnnotationProcessingTest { private FilerProcessor processor; @Override - protected void beforeTest() { + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { this.processor = new FilerProcessor(super.processingEnv); } diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ResourceProcessorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ResourceProcessorTest.java index 35ad93340..868022463 100644 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ResourceProcessorTest.java +++ b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ResourceProcessorTest.java @@ -18,9 +18,13 @@ package io.microsphere.annotation.processor; +import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import javax.tools.FileObject; +import java.lang.reflect.Method; import java.util.Optional; import static io.microsphere.annotation.processor.ResourceProcessor.FOR_READING; @@ -56,14 +60,14 @@ class ResourceProcessorTest extends AbstractAnnotationProcessingTest { private String randomResourceName; @Override - protected void beforeTest() { + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { this.classOutputProcessor = new ResourceProcessor(super.processingEnv, CLASS_OUTPUT); this.sourcePathProcessor = new ResourceProcessor(super.processingEnv, SOURCE_PATH); this.randomResourceName = "test/" + currentTimeMillis() + ".txt"; } @Override - protected void afterTest() { + protected void afterTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext, Object result, Throwable failure) { this.classOutputProcessor.getResource(this.randomResourceName, FOR_WRITING).ifPresent(FileObject::delete); } From 3b27a3d4c05af403a04e7575f554fac4cd94b177 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:03:08 +0800 Subject: [PATCH 020/438] Remove microsphere-annotation-processor module Delete the entire microsphere-annotation-processor module: remove source files under src/main/java (annotation model elements, visitors and various utility classes such as AnnotationUtils, ClassUtils, ConstructorUtils, ElementUtils, ExecutableElementComparator, FieldUtils, LoggerUtils, MemberUtils, MessagerUtils, MethodUtils, TypeUtils, etc.) and corresponding unit tests under src/test/java. This change removes the annotation processing implementation and its tests from the repository. --- .../model/element/StringAnnotationValue.java | 50 - .../util/JSONAnnotationValueVisitor.java | 197 -- .../model/util/JSONElementVisitor.java | 221 -- .../ResolvableAnnotationValueVisitor.java | 200 -- .../processor/util/AnnotationUtils.java | 1871 ------------ .../annotation/processor/util/ClassUtils.java | 109 - .../processor/util/ConstructorUtils.java | 293 -- .../processor/util/ElementUtils.java | 734 ----- .../util/ExecutableElementComparator.java | 93 - .../annotation/processor/util/FieldUtils.java | 763 ----- .../processor/util/LoggerUtils.java | 54 - .../processor/util/MemberUtils.java | 423 --- .../processor/util/MessagerUtils.java | 310 -- .../processor/util/MethodUtils.java | 1165 -------- .../annotation/processor/util/TypeUtils.java | 2505 ----------------- .../AbstractAnnotationProcessingTest.java | 202 -- .../AnnotationProcessingTestProcessor.java | 86 - .../annotation/processor/Compiler.java | 214 -- .../CompilerInvocationInterceptor.java | 57 - .../processor/DefaultTestService.java | 58 - .../processor/GenericTestService.java | 33 - .../annotation/processor/TestAnnotation.java | 73 - .../annotation/processor/TestService.java | 55 - .../annotation/processor/TestServiceImpl.java | 100 - .../annotation/processor/model/Ancestor.java | 35 - .../processor/model/ArrayTypeModel.java | 36 - .../processor/model/CollectionTypeModel.java | 42 - .../annotation/processor/model/Color.java | 46 - .../model/ConfigurationPropertyModel.java | 47 - .../processor/model/MapTypeModel.java | 41 - .../annotation/processor/model/Model.java | 87 - .../annotation/processor/model/Parent.java | 63 - .../processor/model/PrimitiveTypeModel.java | 73 - .../processor/model/SimpleTypeModel.java | 161 -- .../processor/model/StringArrayList.java | 30 - .../element/StringAnnotationValueTest.java | 53 - .../util/JSONAnnotationValueVisitorTest.java | 133 - .../model/util/JSONElementVisitorTest.java | 254 -- .../ResolvableAnnotationValueVisitorTest.java | 214 -- .../processor/util/AnnotationUtilsTest.java | 776 ----- .../processor/util/ClassUtilsTest.java | 52 - .../processor/util/ConstructorUtilsTest.java | 150 - .../processor/util/ElementUtilsTest.java | 350 --- .../util/ExecutableElementComparatorTest.java | 76 - .../processor/util/FieldUtilsTest.java | 413 --- .../processor/util/LoggerUtilsTest.java | 76 - .../processor/util/MemberUtilsTest.java | 174 -- .../processor/util/MessagerUtilsTest.java | 79 - .../processor/util/MethodUtilsTest.java | 506 ---- .../processor/util/TypeUtilsTest.java | 1877 ------------ 50 files changed, 15710 deletions(-) delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/element/StringAnnotationValue.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/JSONAnnotationValueVisitor.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/JSONElementVisitor.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ResolvableAnnotationValueVisitor.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/AnnotationUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ClassUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ConstructorUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ElementUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ExecutableElementComparator.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/FieldUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/LoggerUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MemberUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MessagerUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MethodUtils.java delete mode 100644 microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/TypeUtils.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/AbstractAnnotationProcessingTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/AnnotationProcessingTestProcessor.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/Compiler.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/CompilerInvocationInterceptor.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/DefaultTestService.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/GenericTestService.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestAnnotation.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestService.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestServiceImpl.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Ancestor.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/ArrayTypeModel.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/CollectionTypeModel.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Color.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/ConfigurationPropertyModel.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/MapTypeModel.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Model.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Parent.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/PrimitiveTypeModel.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/SimpleTypeModel.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/StringArrayList.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/element/StringAnnotationValueTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/JSONAnnotationValueVisitorTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/JSONElementVisitorTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/ResolvableAnnotationValueVisitorTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/AnnotationUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ClassUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ConstructorUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ElementUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ExecutableElementComparatorTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/FieldUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/LoggerUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MemberUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MessagerUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MethodUtilsTest.java delete mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/TypeUtilsTest.java diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/element/StringAnnotationValue.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/element/StringAnnotationValue.java deleted file mode 100644 index 42a583f75..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/element/StringAnnotationValue.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model.element; - -import io.microsphere.annotation.Immutable; - -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.AnnotationValueVisitor; - -/** - * {@link AnnotationValue} for String type - * - * @author Mercy - * @see AnnotationValue - * @since 1.0.0 - */ -@Immutable -public class StringAnnotationValue implements AnnotationValue { - - private final String value; - - public StringAnnotationValue(String value) { - this.value = value; - } - - @Override - public Object getValue() { - return this.value; - } - - @Override - public R accept(AnnotationValueVisitor v, P p) { - return v.visitString(this.value, p); - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/JSONAnnotationValueVisitor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/JSONAnnotationValueVisitor.java deleted file mode 100644 index 327c954b0..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/JSONAnnotationValueVisitor.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model.util; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.SimpleAnnotationValueVisitor6; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAttributeName; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValues; -import static io.microsphere.annotation.processor.util.TypeUtils.getTypeName; -import static io.microsphere.constants.SymbolConstants.COMMA_CHAR; -import static io.microsphere.constants.SymbolConstants.LEFT_CURLY_BRACE_CHAR; -import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET_CHAR; -import static io.microsphere.constants.SymbolConstants.RIGHT_CURLY_BRACE_CHAR; -import static io.microsphere.constants.SymbolConstants.RIGHT_SQUARE_BRACKET_CHAR; -import static io.microsphere.json.JSONUtils.append; -import static io.microsphere.json.JSONUtils.appendName; - -/** - * A visitor implementation for converting {@link AnnotationValue} objects into JSON-formatted strings. - * This class extends {@link SimpleAnnotationValueVisitor6} and is designed to work with Java annotation - * processing tools to generate JSON representations of annotation values. - * - *

Example Usage

- *
{@code
- * // Example 1: Visiting a simple annotation value
- * AnnotationValue value = ...; // e.g., a String, int, or boolean value
- * ExecutableElement method = ...; // the method corresponding to the annotation attribute
- * StringBuilder jsonBuilder = new StringBuilder();
- * JSONAnnotationValueVisitor visitor = new JSONAnnotationValueVisitor(jsonBuilder);
- * visitor.visit(value, method); // Result: appends JSON key-value pair to jsonBuilder
- *
- * // Example 2: Visiting an array annotation value
- * List arrayValues = ...; // list of annotation values
- * visitor.visitArray(arrayValues, method); // Result: appends JSON array to jsonBuilder
- *
- * // Example 3: Visiting a nested annotation
- * AnnotationMirror annotationMirror = ...;
- * visitor.visitAnnotation(annotationMirror, method); // Result: appends JSON object to jsonBuilder
- * }
- * - *

This visitor is typically used during annotation processing to serialize annotation values - * into a structured JSON format, useful for configuration or metadata generation purposes. - * - * @author Mercy - * @see SimpleAnnotationValueVisitor6 - * @since 1.0.0 - */ -public class JSONAnnotationValueVisitor extends SimpleAnnotationValueVisitor6 { - - private final StringBuilder jsonBuilder; - - public JSONAnnotationValueVisitor(StringBuilder jsonBuilder) { - super(jsonBuilder); - this.jsonBuilder = jsonBuilder; - } - - - @Override - public StringBuilder visitBoolean(boolean value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitByte(byte value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitChar(char value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitDouble(double value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitFloat(float value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitInt(int value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitLong(long value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitShort(short value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitString(String value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value); - return jsonBuilder; - } - - @Override - public StringBuilder visitType(TypeMirror value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), getTypeName(value)); - return jsonBuilder; - } - - @Override - public StringBuilder visitEnumConstant(VariableElement value, ExecutableElement attributeMethod) { - append(jsonBuilder, getAttributeName(attributeMethod), value.getSimpleName().toString()); - return jsonBuilder; - } - - @Override - public StringBuilder visitAnnotation(AnnotationMirror value, ExecutableElement attributeMethod) { - Map elementValues = getElementValues(value); - Iterator> iterator = elementValues.entrySet().iterator(); - StringBuilder annotationJsonBuilder = new StringBuilder(); - annotationJsonBuilder.append(LEFT_CURLY_BRACE_CHAR); - JSONAnnotationValueVisitor visitor = new JSONAnnotationValueVisitor(annotationJsonBuilder); - while (iterator.hasNext()) { - Entry entry = iterator.next(); - AnnotationValue annotationValue = entry.getValue(); - visitor.visit(annotationValue, entry.getKey()); - if (iterator.hasNext()) { - annotationJsonBuilder.append(COMMA_CHAR); - } - } - annotationJsonBuilder.append(RIGHT_CURLY_BRACE_CHAR); - - return doAppend(attributeMethod, annotationJsonBuilder); - } - - @Override - public StringBuilder visitArray(List values, ExecutableElement attributeMethod) { - StringBuilder arrayJsonBuilder = new StringBuilder(); - - arrayJsonBuilder.append(LEFT_SQUARE_BRACKET_CHAR); - int size = values.size(); - JSONAnnotationValueVisitor visitor = new JSONAnnotationValueVisitor(arrayJsonBuilder); - for (int i = 0; i < size; i++) { - AnnotationValue annotationValue = values.get(i); - annotationValue.accept(visitor, null); - if (i < size - 1) { - arrayJsonBuilder.append(COMMA_CHAR); - } - } - arrayJsonBuilder.append(RIGHT_SQUARE_BRACKET_CHAR); - - return doAppend(attributeMethod, arrayJsonBuilder); - } - - @Override - public StringBuilder visitUnknown(AnnotationValue annotationValue, ExecutableElement attributeMethod) { - return jsonBuilder; - } - - protected StringBuilder doAppend(ExecutableElement attributeMethod, StringBuilder value) { - appendName(this.jsonBuilder, getAttributeName(attributeMethod)) - .append(value); - return this.jsonBuilder; - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/JSONElementVisitor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/JSONElementVisitor.java deleted file mode 100644 index 99856edb3..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/JSONElementVisitor.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model.util; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementVisitor; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.TypeParameterElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.util.AbstractElementVisitor6; -import javax.lang.model.util.ElementKindVisitor6; -import java.util.List; - -import static java.lang.Boolean.FALSE; - -/** - * A specialized {@link ElementVisitor} implementation that traverses Java elements and generates JSON - * representations of the elements' metadata. - * - *

This abstract class extends {@link ElementKindVisitor6} to provide a foundation for creating - * JSON-based representations of various Java language elements such as packages, types, methods, - * fields, and type parameters. - * - *

Example Usage

- *
{@code
- * public class CustomJSONElementVisitor extends JSONElementVisitor {
- *     // Implement specific visit methods to customize JSON generation
- * }
- * }
- * - *

Subclasses should implement or override the appropriate visit methods to customize how - * different Java elements are represented in JSON. - * - * @author Mercy - * @see ElementVisitor - * @see ElementKindVisitor6 - * @see AbstractElementVisitor6 - * @since 1.0.0 - */ -public abstract class JSONElementVisitor extends ElementKindVisitor6 { - - public JSONElementVisitor() { - super(FALSE); - } - - @Override - public final Boolean visitPackage(PackageElement e, StringBuilder jsonBuilder) { - return supportsPackage(e) && doVisitPackage(e, jsonBuilder); - } - - @Override - public final Boolean visitVariable(VariableElement e, StringBuilder stringBuilder) { - return supportsVariable(e) && super.visitVariable(e, stringBuilder); - } - - @Override - public final Boolean visitExecutable(ExecutableElement e, StringBuilder jsonBuilder) { - return supportsExecutable(e) && super.visitExecutable(e, jsonBuilder); - } - - @Override - public final Boolean visitType(TypeElement e, StringBuilder jsonBuilder) { - boolean appended = false; - if (supportsType(e) && super.visitType(e, jsonBuilder)) { - appended = true; - } - - // The declared members of the type element - if (visitMembers(e.getEnclosedElements(), jsonBuilder)) { - appended = true; - } - - return appended; - } - - @Override - public final Boolean visitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { - if (!supports(e)) { - return FALSE; - } - - boolean appended = false; - if (supportsTypeParameter(e) && doVisitTypeParameter(e, jsonBuilder)) { - appended = true; - } - - // The declared members of the type element - if (visitMembers(e.getEnclosedElements(), jsonBuilder)) { - appended = true; - } - - return appended; - } - - protected boolean visitMembers(List members, StringBuilder jsonBuilder) { - boolean appended = false; - for (Element member : members) { - if (member.accept(this, jsonBuilder)) { - appended = true; - } - } - return appended; - } - - /** - * Determines whether the specified element is supported for processing. - * - *

This method can be overridden to provide custom logic for deciding - * if an element should be processed by this visitor.

- * - * @param e the element to check - * @return {@code true} if the element is supported; {@code false} otherwise - */ - protected boolean supports(Element e) { - return true; - } - - /** - * Determines whether the specified package element is supported for processing. - * - *

This method can be overridden to provide custom logic for deciding - * if a package element should be processed by this visitor.

- * - * @param e the package element to check - * @return {@code true} if the package element is supported; {@code false} otherwise - */ - protected boolean supportsPackage(PackageElement e) { - return supports(e); - } - - /** - * Determines whether the specified variable element is supported for processing. - * - *

This method can be overridden to provide custom logic for deciding - * if a variable element should be processed by this visitor.

- * - * @param e the variable element to check - * @return {@code true} if the variable element is supported; {@code false} otherwise - */ - protected boolean supportsVariable(VariableElement e) { - return supports(e); - } - - /** - * Determines whether the specified executable element is supported for processing. - * - *

This method can be overridden to provide custom logic for deciding - * if an executable element should be processed by this visitor.

- * - * @param e the executable element to check - * @return {@code true} if the executable element is supported; {@code false} otherwise - */ - protected boolean supportsExecutable(ExecutableElement e) { - return supports(e); - } - - /** - * Determines whether the specified type element is supported for processing. - * - *

This method can be overridden to provide custom logic for deciding - * if a type element should be processed by this visitor.

- * - * @param e the type element to check - * @return {@code true} if the type element is supported; {@code false} otherwise - */ - protected boolean supportsType(TypeElement e) { - return supports(e); - } - - /** - * Determines whether the specified type parameter element is supported for processing. - * - *

This method can be overridden to provide custom logic for deciding - * if a type parameter element should be processed by this visitor.

- * - * @param e the type parameter element to check - * @return {@code true} if the type parameter element is supported; {@code false} otherwise - */ - protected boolean supportsTypeParameter(TypeParameterElement e) { - return supports(e); - } - - /** - * Visits a package element and appends its JSON representation to the builder. - * - * @param e the package element to visit - * @param jsonBuilder the string builder used to construct the JSON output - * @return {@code true} if any content was appended; {@code false} otherwise - */ - protected boolean doVisitPackage(PackageElement e, StringBuilder jsonBuilder) { - return super.visitPackage(e, jsonBuilder); - } - - /** - * Visits a type parameter element and appends its JSON representation to the builder. - * - * @param e the type parameter element to visit - * @param jsonBuilder the string builder used to construct the JSON output - * @return {@code true} if any content was appended; {@code false} otherwise - */ - protected boolean doVisitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { - return super.visitTypeParameter(e, jsonBuilder); - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ResolvableAnnotationValueVisitor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ResolvableAnnotationValueVisitor.java deleted file mode 100644 index 888c2f7aa..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ResolvableAnnotationValueVisitor.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model.util; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.AnnotationValueVisitor; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.ArrayType; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.SimpleAnnotationValueVisitor6; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAttributeName; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValues; -import static io.microsphere.annotation.processor.util.ClassUtils.loadClass; -import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; -import static io.microsphere.reflect.MethodUtils.findMethod; -import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; -import static io.microsphere.util.ArrayUtils.newArray; -import static io.microsphere.util.ClassLoaderUtils.resolveClass; -import static java.lang.Enum.valueOf; -import static java.lang.reflect.Array.set; - -/** - * A visitor for resolving annotation values into their corresponding runtime representations. - * - *

This class extends {@link SimpleAnnotationValueVisitor6} to process annotation values and convert - * them into appropriate Java objects such as primitives, strings, enums, classes, annotations, and arrays. - * - *

Example Usage

- *
{@code
- * // Create an instance with default settings
- * ResolvableAnnotationValueVisitor visitor = new ResolvableAnnotationValueVisitor();
- *
- * // Visit an annotation value
- * AnnotationValue annotationValue = ...; // Obtain from an annotation mirror
- * Object resolvedValue = annotationValue.accept(visitor, executableElement);
- * }
- * - *

Custom behavior examples

- *
{@code
- * // Create an instance that represents Class values as strings
- * ResolvableAnnotationValueVisitor visitor = new ResolvableAnnotationValueVisitor(true);
- *
- * // Create an instance that handles nested annotations as maps
- * ResolvableAnnotationValueVisitor visitor = new ResolvableAnnotationValueVisitor(false, true);
- * }
- * - * @author Mercy - * @see AnnotationValueVisitor - * @since 1.0.0 - */ -public class ResolvableAnnotationValueVisitor extends SimpleAnnotationValueVisitor6 { - - private static final Class ANNOTATION_PARSER_CLASS = resolveClass("sun.reflect.annotation.AnnotationParser"); - - private static final Method annotationForMapMethod = findMethod(ANNOTATION_PARSER_CLASS, "annotationForMap", Class.class, Map.class); - - private static final boolean DEFAULT_CLASS_VALUES_AS_STRING = false; - - private static final boolean DEFAULT_NESTED_ANNOTATIONS_AS_MAP = false; - - private final boolean classValuesAsString; - - private final boolean nestedAnnotationsAsMap; - - public ResolvableAnnotationValueVisitor() { - this(DEFAULT_CLASS_VALUES_AS_STRING, DEFAULT_NESTED_ANNOTATIONS_AS_MAP); - } - - public ResolvableAnnotationValueVisitor(boolean classValuesAsString) { - this(classValuesAsString, DEFAULT_NESTED_ANNOTATIONS_AS_MAP); - } - - public ResolvableAnnotationValueVisitor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) { - this.classValuesAsString = classValuesAsString; - this.nestedAnnotationsAsMap = nestedAnnotationsAsMap; - } - - @Override - public Object visitBoolean(boolean b, ExecutableElement attributeMethod) { - return b; - } - - @Override - public Object visitByte(byte b, ExecutableElement attributeMethod) { - return b; - } - - @Override - public Object visitChar(char c, ExecutableElement attributeMethod) { - return c; - } - - @Override - public Object visitDouble(double d, ExecutableElement attributeMethod) { - return d; - } - - @Override - public Object visitFloat(float f, ExecutableElement attributeMethod) { - return f; - } - - @Override - public Object visitInt(int i, ExecutableElement attributeMethod) { - return i; - } - - @Override - public Object visitLong(long i, ExecutableElement attributeMethod) { - return i; - } - - @Override - public Object visitShort(short s, ExecutableElement attributeMethod) { - return s; - } - - @Override - public Object visitString(String s, ExecutableElement attributeMethod) { - return s; - } - - @Override - public Object visitType(TypeMirror t, ExecutableElement attributeMethod) { - return classValuesAsString ? t.toString() : loadClass(t); - } - - @Override - public Object visitEnumConstant(VariableElement c, ExecutableElement attributeMethod) { - Class enumClass = loadClass(c.asType()); - return valueOf(enumClass, c.toString()); - } - - @Override - public Object visitAnnotation(AnnotationMirror a, ExecutableElement attributeMethod) { - Map elementValues = getElementValues(a); - Map attributesMap = newFixedLinkedHashMap(elementValues.size()); - for (Entry entry : elementValues.entrySet()) { - ExecutableElement method = entry.getKey(); - String attributeName = getAttributeName(method); - Object attributeValue = entry.getValue().accept(this, method); - attributesMap.put(attributeName, attributeValue); - } - - if (nestedAnnotationsAsMap) { - return attributesMap; - } - - Class annotationClass = loadClass(a.getAnnotationType()); - return invokeStaticMethod(annotationForMapMethod, annotationClass, attributesMap); - } - - @Override - public Object visitArray(List values, ExecutableElement attributeMethod) { - int size = values.size(); - Class componentType = getComponentType(attributeMethod); - Object array = newArray(componentType, size); - for (int i = 0; i < size; i++) { - AnnotationValue value = values.get(i); - Object attributeValue = value.accept(this, attributeMethod); - set(array, i, attributeValue); - } - return array; - } - - Class getComponentType(ExecutableElement attributeMethod) { - if (classValuesAsString) { - return String.class; - } - ArrayType arrayType = (ArrayType) attributeMethod.getReturnType(); - return loadClass(arrayType.getComponentType()); - } - - @Override - public Object visitUnknown(AnnotationValue av, ExecutableElement attributeMethod) { - return av; - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/AnnotationUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/AnnotationUtils.java deleted file mode 100644 index 4df0bdfc6..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/AnnotationUtils.java +++ /dev/null @@ -1,1871 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.Immutable; -import io.microsphere.annotation.Nonnull; -import io.microsphere.annotation.Nullable; -import io.microsphere.annotation.processor.model.util.ResolvableAnnotationValueVisitor; -import io.microsphere.util.Utils; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.AnnotatedConstruct; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.AnnotationValueVisitor; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; -import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.function.Predicate; - -import static io.microsphere.annotation.processor.util.MethodUtils.findDeclaredMethods; -import static io.microsphere.annotation.processor.util.MethodUtils.getDeclaredMethods; -import static io.microsphere.annotation.processor.util.MethodUtils.getMethodName; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllTypeElements; -import static io.microsphere.annotation.processor.util.TypeUtils.getTypeElement; -import static io.microsphere.annotation.processor.util.TypeUtils.isSameType; -import static io.microsphere.annotation.processor.util.TypeUtils.ofTypeElement; -import static io.microsphere.collection.CollectionUtils.isEmpty; -import static io.microsphere.collection.CollectionUtils.size; -import static io.microsphere.collection.MapUtils.immutableEntry; -import static io.microsphere.collection.MapUtils.isEmpty; -import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; -import static io.microsphere.lang.function.Streams.filterAll; -import static io.microsphere.util.ArrayUtils.isNotEmpty; -import static io.microsphere.util.StringUtils.isBlank; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableMap; -import static java.util.stream.Collectors.toList; - -/** - * A utility interface for working with annotations in the {@code javax.lang.model.*} package. - *

- * This interface provides methods to retrieve, filter, and check annotations and their attributes - * from various constructs such as {@link Element}, {@link TypeMirror}, and others commonly used - * in annotation processing. - *

- * - *

Example Usage

- *
{@code
- * // Retrieve a specific annotation from an element
- * AnnotationMirror annotation = AnnotationUtils.getAnnotation(element, MyAnnotation.class);
- *
- * // Retrieve all annotations from a type
- * List annotations = AnnotationUtils.getAnnotations(typeMirror);
- *
- * // Check if an annotation is present on an element
- * boolean present = AnnotationUtils.isAnnotationPresent(element, MyAnnotation.class);
- *
- * // Find meta-annotations on an annotation
- * AnnotationMirror metaAnnotation = AnnotationUtils.findMetaAnnotation(annotation, MyMetaAnnotation.class);
- * }
- * - *

- * This interface is intended to be implemented by classes that provide static utility methods - * for handling annotations during annotation processing. It helps in organizing and reusing - * common operations related to annotations. - *

- * - * @author Mercy - * @since 1.0.0 - */ -public interface AnnotationUtils extends Utils { - - /** - * The name of the attribute method : value() - */ - String VALUE_ATTRIBUTE_NAME = "value"; - - /** - * The default {@link AnnotationValueVisitor} - */ - AnnotationValueVisitor DEFAULT_ANNOTATION_VALUE_VISITOR = new ResolvableAnnotationValueVisitor(); - - /** - * The empty {@link ElementType} array - */ - @Immutable - ElementType[] EMPTY_ELEMENT_TYPE_ARRAY = new ElementType[0]; - - boolean WITH_DEFAULT = true; - - /** - * Retrieves the first {@link AnnotationMirror} of the specified annotation class from the given - * {@link AnnotatedConstruct}. If either the construct or the annotation class is {@code null}, - * this method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * // Get an annotation directly present on a class
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * AnnotationMirror annotation = AnnotationUtils.getAnnotation(typeElement, MyAnnotation.class);
-     *
-     * // Get an annotation from an element that may inherit annotations from its parent
-     * Element element = ...; // obtain an Element
-     * annotation = AnnotationUtils.getAnnotation(element, MyAnnotation.class);
-     * }
- * - * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} - * @param annotationClass the annotation class to look for, may be {@code null} - * @return the first matching {@link AnnotationMirror}, or {@code null} if none found - */ - static AnnotationMirror getAnnotation(AnnotatedConstruct annotatedConstruct, Class annotationClass) { - if (annotatedConstruct == null || annotationClass == null) { - return null; - } - return getAnnotation(annotatedConstruct, annotationClass.getName()); - } - - /** - * Retrieves the first {@link AnnotationMirror} of the specified annotation class name from the given - * {@link AnnotatedConstruct}. If either the construct or the annotation class name is {@code null}, - * this method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * // Get an annotation by its class name from an element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * String annotationClassName = "com.example.MyAnnotation";
-     * AnnotationMirror annotation = AnnotationUtils.getAnnotation(typeElement, annotationClassName);
-     *
-     * // Get an annotation from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotation = AnnotationUtils.getAnnotation(methodElement, "com.example.AnotherAnnotation");
-     * }
- * - * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} - * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} - * @return the first matching {@link AnnotationMirror}, or {@code null} if none found - */ - static AnnotationMirror getAnnotation(AnnotatedConstruct annotatedConstruct, CharSequence annotationClassName) { - if (annotatedConstruct == null || annotationClassName == null) { - return null; - } - List annotations = getAnnotations(annotatedConstruct, annotationClassName); - return annotations.isEmpty() ? null : annotations.get(0); - } - - /** - * Retrieves all {@link AnnotationMirror} instances from the given {@link AnnotatedConstruct}. - * If the annotated construct is {@code null}, this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve all annotations from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * List annotations = AnnotationUtils.getAnnotations(typeElement);
-     *
-     * // Retrieve annotations from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotations = AnnotationUtils.getAnnotations(methodElement);
-     * }
- * - * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAnnotations(AnnotatedConstruct annotatedConstruct) { - if (annotatedConstruct == null) { - return emptyList(); - } - return findAnnotations(annotatedConstruct, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotation class from the given - * {@link AnnotatedConstruct}. If either the construct or the annotation class is {@code null}, - * this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve all annotations of a specific type from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * List annotations = AnnotationUtils.getAnnotations(typeElement, MyAnnotation.class);
-     *
-     * // Retrieve annotations from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotations = AnnotationUtils.getAnnotations(methodElement, AnotherAnnotation.class);
-     * }
- * - * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} - * @param annotationClass the annotation class to look for, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAnnotations(AnnotatedConstruct annotatedConstruct, Class annotationClass) { - if (annotatedConstruct == null || annotationClass == null) { - return emptyList(); - } - return getAnnotations(annotatedConstruct, annotationClass.getTypeName()); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotation class name from the given - * {@link AnnotatedConstruct}. If either the construct or the annotation class name is {@code null}, - * this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve annotations by their class name from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * String annotationClassName = "com.example.MyAnnotation";
-     * List annotations = AnnotationUtils.getAnnotations(typeElement, annotationClassName);
-     *
-     * // Retrieve annotations from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotations = AnnotationUtils.getAnnotations(methodElement, "com.example.AnotherAnnotation");
-     * }
- * - * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} - * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAnnotations(AnnotatedConstruct annotatedConstruct, CharSequence annotationClassName) { - if (annotatedConstruct == null || annotationClassName == null) { - return emptyList(); - } - return findAnnotations(annotatedConstruct, annotation -> matchesAnnotationTypeName(annotation, annotationClassName)); - } - - /** - * Retrieves all {@link AnnotationMirror} instances from the given {@link TypeMirror}. - * If the type mirror is {@code null}, this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve all annotations from a type mirror
-     * TypeMirror type = ...; // obtain a TypeMirror
-     * List annotations = AnnotationUtils.getAllAnnotations(type);
-     *
-     * // Handle cases where the type might be null
-     * annotations = AnnotationUtils.getAllAnnotations(null); // returns empty list
-     * }
- * - * @param type the type mirror to search for annotations, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAllAnnotations(TypeMirror type) { - if (type == null) { - return emptyList(); - } - return getAllAnnotations(ofTypeElement(type)); - } - - /** - * Retrieves all {@link AnnotationMirror} instances from the given {@link Element}. - * If the element is {@code null}, this method returns an empty list. - * - *

This method is designed to provide a consistent way of retrieving annotations - * across different constructs in the annotation processing framework.

- * - *

Example Usage

- *
{@code
-     * // Retrieve all annotations from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * List annotations = AnnotationUtils.getAllAnnotations(typeElement);
-     *
-     * // Retrieve annotations from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotations = AnnotationUtils.getAllAnnotations(methodElement);
-     *
-     * // Handle cases where the element might be null
-     * annotations = AnnotationUtils.getAllAnnotations(null); // returns empty list
-     * }
- * - * @param element the annotated element to search for annotations, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAllAnnotations(Element element) { - if (element == null) { - return emptyList(); - } - return findAllAnnotations(element, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotation class from the given - * {@link TypeMirror}. If either the type or the annotation class is {@code null}, this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve annotations from a type mirror
-     * TypeMirror type = ...; // obtain a TypeMirror
-     * List annotations = AnnotationUtils.getAllAnnotations(type, MyAnnotation.class);
-     *
-     * // Handle cases where the type might be null
-     * annotations = AnnotationUtils.getAllAnnotations(null, MyAnnotation.class); // returns empty list
-     *
-     * // Handle cases where the annotation class might be null
-     * annotations = AnnotationUtils.getAllAnnotations(type, null); // returns empty list
-     * }
- * - * @param type the type mirror to search for annotations, may be {@code null} - * @param annotationClass the annotation class to look for, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAllAnnotations(TypeMirror type, Class annotationClass) { - if (type == null || annotationClass == null) { - return emptyList(); - } - return getAllAnnotations(ofTypeElement(type), annotationClass); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotation class from the given - * {@link Element}. If either the element or the annotation class is {@code null}, - * this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve annotations from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * List annotations = AnnotationUtils.getAllAnnotations(typeElement, MyAnnotation.class);
-     *
-     * // Retrieve annotations from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotations = AnnotationUtils.getAllAnnotations(methodElement, AnotherAnnotation.class);
-     *
-     * // Handle cases where the element might be null
-     * annotations = AnnotationUtils.getAllAnnotations(null, MyAnnotation.class); // returns empty list
-     *
-     * // Handle cases where the annotation class might be null
-     * annotations = AnnotationUtils.getAllAnnotations(typeElement, null); // returns empty list
-     * }
- * - * @param element the annotated element to search for annotations, may be {@code null} - * @param annotationClass the annotation class to look for, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAllAnnotations(Element element, Class annotationClass) { - if (element == null || annotationClass == null) { - return emptyList(); - } - return getAllAnnotations(element, annotationClass.getTypeName()); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotation class name from the given - * {@link TypeMirror}. If either the type or the annotation class name is {@code null}, - * this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve annotations from a type mirror
-     * TypeMirror type = ...; // obtain a TypeMirror
-     * String annotationClassName = "com.example.MyAnnotation";
-     * List annotations = AnnotationUtils.getAllAnnotations(type, annotationClassName);
-     *
-     * // Handle cases where the type might be null
-     * annotations = AnnotationUtils.getAllAnnotations(null, annotationClassName); // returns empty list
-     *
-     * // Handle cases where the annotation class name might be null
-     * annotations = AnnotationUtils.getAllAnnotations(type, null); // returns empty list
-     * }
- * - * @param type the type mirror to search for annotations, may be {@code null} - * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAllAnnotations(TypeMirror type, CharSequence annotationClassName) { - if (type == null || annotationClassName == null) { - return emptyList(); - } - return getAllAnnotations(ofTypeElement(type), annotationClassName); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotation class name from the given - * {@link Element}. If either the element or the annotation class name is {@code null}, - * this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve annotations by their class name from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * String annotationClassName = "com.example.MyAnnotation";
-     * List annotations = AnnotationUtils.getAllAnnotations(typeElement, annotationClassName);
-     *
-     * // Retrieve annotations from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotations = AnnotationUtils.getAllAnnotations(methodElement, "com.example.AnotherAnnotation");
-     *
-     * // Handle cases where the element might be null
-     * annotations = AnnotationUtils.getAllAnnotations(null, annotationClassName); // returns empty list
-     *
-     * // Handle cases where the annotation class name might be null
-     * annotations = AnnotationUtils.getAllAnnotations(typeElement, null); // returns empty list
-     * }
- * - * @param element the annotated element to search for annotations, may be {@code null} - * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAllAnnotations(Element element, CharSequence annotationClassName) { - if (element == null || annotationClassName == null) { - return emptyList(); - } - return findAllAnnotations(element, annotation -> matchesAnnotationTypeName(annotation, annotationClassName)); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotated type from the given - * {@link ProcessingEnvironment}. If either the processing environment or the annotated type is {@code null}, - * this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * // Retrieve annotations for a specific type from the processing environment
-     * ProcessingEnvironment processingEnv = ...; // obtain a ProcessingEnvironment instance
-     * Type annotatedType = MyAnnotation.class; // specify the annotation type
-     * List annotations = AnnotationUtils.getAllAnnotations(processingEnv, annotatedType);
-     *
-     * // Handle cases where the processing environment might be null
-     * annotations = AnnotationUtils.getAllAnnotations(null, annotatedType); // returns empty list
-     *
-     * // Handle cases where the annotated type might be null
-     * annotations = AnnotationUtils.getAllAnnotations(processingEnv, null); // returns empty list
-     * }
- * - * @param processingEnv the processing environment used to retrieve annotations, may be {@code null} - * @param annotatedType the annotated type to search for annotations, may be {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List getAllAnnotations(ProcessingEnvironment processingEnv, Type annotatedType) { - if (processingEnv == null || annotatedType == null) { - return emptyList(); - } - return findAllAnnotations(processingEnv, annotatedType, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves the first {@link AnnotationMirror} of the specified annotation class from the given - * {@link TypeMirror}. If either the type or the annotation class is {@code null}, - * this method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * // Retrieve an annotation from a type mirror
-     * TypeMirror type = ...; // obtain a TypeMirror instance
-     * AnnotationMirror annotation = AnnotationUtils.findAnnotation(type, MyAnnotation.class);
-     *
-     * // Handle cases where the type might be null
-     * annotation = AnnotationUtils.findAnnotation(null, MyAnnotation.class); // returns null
-     *
-     * // Handle cases where the annotation class might be null
-     * annotation = AnnotationUtils.findAnnotation(type, null); // returns null
-     * }
- * - * @param type the type mirror to search for annotations, may be {@code null} - * @param annotationClass the annotation class to look for, may be {@code null} - * @return the first matching {@link AnnotationMirror}, or {@code null} if none found - */ - static AnnotationMirror findAnnotation(TypeMirror type, Class annotationClass) { - if (type == null || annotationClass == null) { - return null; - } - return findAnnotation(type, annotationClass.getTypeName()); - } - - /** - * Retrieves the first {@link AnnotationMirror} of the specified annotation class name from the given - * {@link TypeMirror}. If either the type or the annotation class name is {@code null}, - * this method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * // Retrieve an annotation by its class name from a type mirror
-     * TypeMirror type = ...; // obtain a TypeMirror instance
-     * String annotationClassName = "com.example.MyAnnotation";
-     * AnnotationMirror annotation = AnnotationUtils.findAnnotation(type, annotationClassName);
-     *
-     * // Handle cases where the type might be null
-     * annotation = AnnotationUtils.findAnnotation(null, annotationClassName); // returns null
-     *
-     * // Handle cases where the annotation class name might be null
-     * annotation = AnnotationUtils.findAnnotation(type, null); // returns null
-     * }
- * - * @param type the type mirror to search for annotations, may be {@code null} - * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} - * @return the first matching {@link AnnotationMirror}, or {@code null} if none found - */ - static AnnotationMirror findAnnotation(TypeMirror type, CharSequence annotationClassName) { - if (type == null || annotationClassName == null) { - return null; - } - return findAnnotation(ofTypeElement(type), annotationClassName); - } - - /** - * Retrieves the first {@link AnnotationMirror} of the specified annotation class from the given - * {@link Element}. If either the element or the annotation class is {@code null}, - * this method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * // Retrieve an annotation from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * AnnotationMirror annotation = AnnotationUtils.findAnnotation(typeElement, MyAnnotation.class);
-     *
-     * // Retrieve an annotation from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotation = AnnotationUtils.findAnnotation(methodElement, AnotherAnnotation.class);
-     *
-     * // Handle cases where the element might be null
-     * annotation = AnnotationUtils.findAnnotation(null, MyAnnotation.class); // returns null
-     *
-     * // Handle cases where the annotation class might be null
-     * annotation = AnnotationUtils.findAnnotation(typeElement, null); // returns null
-     * }
- * - * @param element the annotated element to search for annotations, may be {@code null} - * @param annotationClass the annotation class to look for, may be {@code null} - * @return the first matching {@link AnnotationMirror}, or {@code null} if none found - */ - static AnnotationMirror findAnnotation(Element element, Class annotationClass) { - if (element == null || annotationClass == null) { - return null; - } - return findAnnotation(element, annotationClass.getTypeName()); - } - - /** - * Retrieves the first {@link AnnotationMirror} of the specified annotation class name from the given - * {@link Element}. If either the element or the annotation class name is {@code null}, - * this method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * // Retrieve an annotation by its class name from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * String annotationClassName = "com.example.MyAnnotation";
-     * AnnotationMirror annotation = AnnotationUtils.findAnnotation(typeElement, annotationClassName);
-     *
-     * // Retrieve an annotation from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * annotation = AnnotationUtils.findAnnotation(methodElement, "com.example.AnotherAnnotation");
-     *
-     * // Handle cases where the element might be null
-     * annotation = AnnotationUtils.findAnnotation(null, annotationClassName); // returns null
-     *
-     * // Handle cases where the annotation class name might be null
-     * annotation = AnnotationUtils.findAnnotation(typeElement, null); // returns null
-     * }
- * - * @param element the annotated element to search for annotations, may be {@code null} - * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} - * @return the first matching {@link AnnotationMirror}, or {@code null} if none found - */ - static AnnotationMirror findAnnotation(Element element, CharSequence annotationClassName) { - if (element == null || annotationClassName == null) { - return null; - } - List annotations = findAllAnnotations(element, annotation -> matchesAnnotationTypeName(annotation, annotationClassName)); - return isEmpty(annotations) ? null : annotations.get(0); - } - - /** - * Retrieves the first meta-annotation of the specified meta-annotation class from the given annotated element. - * A meta-annotation is an annotation that is present on another annotation. If either the annotated element - * or the meta-annotation class is {@code null}, this method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * // Retrieve a meta-annotation from an annotation on a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * AnnotationMirror metaAnnotation = AnnotationUtils.findMetaAnnotation(typeElement, MyMetaAnnotation.class);
-     *
-     * // Retrieve a meta-annotation from an annotation on a method element
-     * Element methodElement = ...; // obtain a method Element
-     * metaAnnotation = AnnotationUtils.findMetaAnnotation(methodElement, AnotherMetaAnnotation.class);
-     *
-     * // Handle cases where the element might be null
-     * metaAnnotation = AnnotationUtils.findMetaAnnotation(null, MyMetaAnnotation.class); // returns null
-     *
-     * // Handle cases where the meta-annotation class might be null
-     * metaAnnotation = AnnotationUtils.findMetaAnnotation(typeElement, null); // returns null
-     * }
- * - * @param annotatedConstruct the annotated element to search for meta-annotations, may be {@code null} - * @param metaAnnotationClass the annotation class to look for as a meta-annotation, may be {@code null} - * @return the first matching meta-{@link AnnotationMirror}, or {@code null} if none found - */ - static AnnotationMirror findMetaAnnotation(Element annotatedConstruct, Class metaAnnotationClass) { - if (annotatedConstruct == null || metaAnnotationClass == null) { - return null; - } - return findMetaAnnotation(annotatedConstruct, metaAnnotationClass.getName()); - } - - /** - * Retrieves the first meta-annotation of the specified meta-annotation class name from the given annotated element. - * A meta-annotation is an annotation that is present on another annotation. If either the annotated element - * or the meta-annotation class name is {@code null}, this method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * // Retrieve a meta-annotation by its class name from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * String metaAnnotationClassName = "com.example.MyMetaAnnotation";
-     * AnnotationMirror metaAnnotation = AnnotationUtils.findMetaAnnotation(typeElement, metaAnnotationClassName);
-     *
-     * // Retrieve a meta-annotation from a method element
-     * Element methodElement = ...; // obtain a method Element
-     * metaAnnotation = AnnotationUtils.findMetaAnnotation(methodElement, "com.example.AnotherMetaAnnotation");
-     *
-     * // Handle cases where the element might be null
-     * metaAnnotation = AnnotationUtils.findMetaAnnotation(null, metaAnnotationClassName); // returns null
-     *
-     * // Handle cases where the meta-annotation class name might be null
-     * metaAnnotation = AnnotationUtils.findMetaAnnotation(typeElement, null); // returns null
-     * }
- * - * @param annotatedConstruct the annotated element to search for meta-annotations, may be {@code null} - * @param metaAnnotationClassName the fully qualified class name of the meta-annotation to look for, may be {@code null} - * @return the first matching meta-{@link AnnotationMirror}, or {@code null} if none found - */ - static AnnotationMirror findMetaAnnotation(Element annotatedConstruct, CharSequence metaAnnotationClassName) { - if (annotatedConstruct == null || metaAnnotationClassName == null) { - return null; - } - - AnnotationMirror metaAnnotation = null; - - List annotations = getAllAnnotations(annotatedConstruct); - int size = size(annotations); - - for (int i = 0; i < size; i++) { - AnnotationMirror annotation = annotations.get(i); - if ((metaAnnotation = findAnnotation(annotation.getAnnotationType(), metaAnnotationClassName)) != null) { - break; - } - } - - return metaAnnotation; - } - - /** - * Checks if the specified annotation is present on the given element, either directly or as a meta-annotation. - * - *

Example Usage

- *
{@code
-     * // Check if an annotation is present on a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * boolean present = AnnotationUtils.isAnnotationPresent(typeElement, MyAnnotation.class);
-     *
-     * // Check if an annotation is present on a method element
-     * Element methodElement = ...; // obtain a method Element
-     * present = AnnotationUtils.isAnnotationPresent(methodElement, AnotherAnnotation.class);
-     *
-     * // Handle cases where the element might be null
-     * present = AnnotationUtils.isAnnotationPresent(null, MyAnnotation.class); // returns false
-     *
-     * // Handle cases where the annotation class might be null
-     * present = AnnotationUtils.isAnnotationPresent(typeElement, null); // returns false
-     * }
- * - * @param element the element to check for the presence of the annotation; may be {@code null} - * @param annotationClass the annotation class to look for; may be {@code null} - * @return {@code true} if the annotation is present (either directly or as a meta-annotation), - * {@code false} otherwise or if either parameter is {@code null} - */ - static boolean isAnnotationPresent(Element element, Class annotationClass) { - if (element == null || annotationClass == null) { - return false; - } - return findAnnotation(element, annotationClass) != null || findMetaAnnotation(element, annotationClass) != null; - } - - /** - * Checks if the specified annotation (by class name) is present on the given element, either directly or as a meta-annotation. - * - *

Example Usage

- *
{@code
-     * // Check if an annotation is present on a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * String annotationClassName = "com.example.MyAnnotation";
-     * boolean present = AnnotationUtils.isAnnotationPresent(typeElement, annotationClassName);
-     *
-     * // Check if an annotation is present on a method element
-     * Element methodElement = ...; // obtain a method Element
-     * present = AnnotationUtils.isAnnotationPresent(methodElement, "com.example.AnotherAnnotation");
-     *
-     * // Handle cases where the element might be null
-     * present = AnnotationUtils.isAnnotationPresent(null, annotationClassName); // returns false
-     *
-     * // Handle cases where the annotation class name might be null
-     * present = AnnotationUtils.isAnnotationPresent(typeElement, null); // returns false
-     * }
- * - * @param element the element to check for the presence of the annotation; may be {@code null} - * @param annotationClassName the fully qualified class name of the annotation to look for; may be {@code null} - * @return {@code true} if the annotation is present (either directly or as a meta-annotation), - * {@code false} otherwise or if either parameter is {@code null} - */ - static boolean isAnnotationPresent(Element element, CharSequence annotationClassName) { - if (element == null || annotationClassName == null) { - return false; - } - return findAnnotation(element, annotationClassName) != null || findMetaAnnotation(element, annotationClassName) != null; - } - - /** - * Retrieves all {@link AnnotationMirror} instances from the given {@link AnnotatedConstruct} - * that match the provided annotation filters. - * - *

If the annotated construct is {@code null}, this method returns an empty list. - * If no annotation filters are provided, all annotations present on the construct are returned. - * Otherwise, only annotations that satisfy all the provided predicates are included in the result.

- * - *

Example Usage

- *
{@code
-     * // Retrieve all annotations from a class element
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * List allAnnotations = AnnotationUtils.findAnnotations(typeElement);
-     *
-     * // Retrieve annotations that match a specific condition
-     * List filteredAnnotations = AnnotationUtils.findAnnotations(typeElement,
-     *     annotation -> "com.example.MyAnnotation".contentEquals(annotation.getAnnotationType().asElement().toString()));
-     *
-     * // Retrieve annotations that match multiple conditions
-     * List multiFilteredAnnotations = AnnotationUtils.findAnnotations(typeElement,
-     *     annotation -> isAnnotationPresent(annotation, "com.example.MetaAnnotation"),
-     *     annotation -> annotation.getElementValues().size() > 1);
-     *
-     * // Handle cases where the annotated construct is null
-     * List annotations = AnnotationUtils.findAnnotations(null); // returns empty list
-     * }
- * - * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} - * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} - * @return a non-null immutable list of matching {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List findAnnotations(AnnotatedConstruct annotatedConstruct, Predicate... annotationFilters) { - if (annotatedConstruct == null) { - return emptyList(); - } - - List annotations = (List) annotatedConstruct.getAnnotationMirrors(); - if (isEmpty(annotations)) { - return emptyList(); - } - - if (isNotEmpty(annotationFilters)) { - annotations = filterAll(annotations, annotationFilters); - } - - return annotations.isEmpty() ? emptyList() : unmodifiableList(annotations); - } - - /** - * Retrieves all {@link AnnotationMirror} instances from the given {@link TypeMirror}, applying the specified - * annotation filters to narrow down the results. If the type mirror is {@code null}, this method returns an empty list. - * - *

This method delegates to {@link #findAllAnnotations(TypeElement, Predicate[])} after converting the - * {@link TypeMirror} to a {@link TypeElement} using {@link TypeUtils#ofTypeElement(TypeMirror)}. - * - * @param type the type mirror to search for annotations, may be {@code null} - * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} - * @return a non-null immutable list of matching {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List findAllAnnotations(TypeMirror type, Predicate... annotationFilters) { - if (type == null) { - return emptyList(); - } - return findAllAnnotations(ofTypeElement(type), annotationFilters); - } - - @Nonnull - @Immutable - static List findAllAnnotations(TypeElement element, Predicate... annotationFilters) { - if (element == null) { - return emptyList(); - } - List typeElements = getAllTypeElements(element); - - List annotations = typeElements.stream() - .map(AnnotationUtils::getAnnotations) - .flatMap(Collection::stream) - .collect(toList()); - - if (isNotEmpty(annotationFilters)) { - annotations = filterAll(annotations, annotationFilters); - } - - return isEmpty(annotations) ? emptyList() : unmodifiableList(annotations); - } - - /** - * Retrieves all {@link AnnotationMirror} instances from the given {@link Element}, applying the specified - * annotation filters to narrow down the results. If the element is {@code null}, this method returns an empty list. - * - *

This method attempts to resolve the element into a {@link TypeElement}. If successful, it delegates to - * {@link #findAllAnnotations(TypeElement, Predicate[])} to retrieve annotations from the type hierarchy. - * Otherwise, it falls back to using {@link #findAnnotations(AnnotatedConstruct, Predicate[])} directly on the element.

- * - * @param element the annotated element to search for annotations, may be {@code null} - * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} - * @return a non-null immutable list of matching {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List findAllAnnotations(Element element, Predicate... annotationFilters) { - if (element == null) { - return emptyList(); - } - - TypeElement typeElement = ofTypeElement(element); - - if (typeElement == null) { - return findAnnotations(element, annotationFilters); - } - - return findAllAnnotations(typeElement, annotationFilters); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotated type from the given - * {@link ProcessingEnvironment}. If either the processing environment or the annotated type is {@code null}, - * this method returns an empty list. - * - *

This method uses the fully qualified type name of the provided {@link Type} to locate the corresponding - * annotations. It delegates to the overloaded method that accepts a {@link CharSequence} for the type name.

- * - * @param processingEnv the processing environment used to retrieve annotations, may be {@code null} - * @param annotatedType the annotated type to search for annotations, may be {@code null} - * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} - * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List findAllAnnotations(ProcessingEnvironment processingEnv, Type annotatedType, Predicate... annotationFilters) { - if (processingEnv == null || annotatedType == null) { - return emptyList(); - } - return findAllAnnotations(processingEnv, annotatedType.getTypeName(), annotationFilters); - } - - /** - * Retrieves all {@link AnnotationMirror} instances of the specified annotated type name from the given - * {@link ProcessingEnvironment}. If either the processing environment or the annotated type name is {@code null}, - * this method returns an empty list. - * - *

This method resolves the annotated type by delegating to {@link TypeUtils#getTypeElement(ProcessingEnvironment, CharSequence)}, - * and then uses the resolved type to find annotations with the provided filters.

- * - * @param processingEnv the processing environment used to retrieve annotations, may be {@code null} - * @param annotatedTypeName the fully qualified class name of the annotation to look for, may be {@code null} - * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} - * @return a non-null immutable list of matching {@link AnnotationMirror} instances; never {@code null} - */ - @Nonnull - @Immutable - static List findAllAnnotations(ProcessingEnvironment processingEnv, CharSequence annotatedTypeName, Predicate... annotationFilters) { - if (processingEnv == null || annotatedTypeName == null) { - return emptyList(); - } - return findAllAnnotations(getTypeElement(processingEnv, annotatedTypeName), annotationFilters); - } - - /** - * Checks if the given annotation mirror has the same type as the specified {@link Type}. - * - *

This method compares the fully qualified name of the annotation mirror's type with - * the provided type's name. Returns {@code false} if either parameter is {@code null}.

- * - *

Example Usage

- *
{@code
-     * AnnotationMirror annotationMirror = ...; // obtain an AnnotationMirror instance
-     * Type annotationType = ...; // obtain a Type instance
-     *
-     * // Check if both are non-null and the types match
-     * boolean isMatch = AnnotationUtils.matchesAnnotationType(annotationMirror, annotationType);
-     *
-     * // Handle cases where the annotation mirror might be null
-     * isMatch = AnnotationUtils.matchesAnnotationType(null, annotationType); // returns false
-     *
-     * // Handle cases where the annotation type might be null
-     * isMatch = AnnotationUtils.matchesAnnotationType(annotationMirror, null); // returns false
-     * }
- * - * @param annotationMirror the annotation mirror to compare; may be {@code null} - * @param annotationType the target type to match against; may be {@code null} - * @return {@code true} if both parameters are non-null and their types match by name; - * {@code false} otherwise - */ - static boolean matchesAnnotationType(AnnotationMirror annotationMirror, Type annotationType) { - if (annotationMirror == null || annotationType == null) { - return false; - } - return matchesAnnotationTypeName(annotationMirror, annotationType.getTypeName()); - } - - /** - * Checks if the given annotation mirror has the same type as the specified annotation class name. - * - *

This method compares the fully qualified name of the annotation mirror's type with - * the provided annotation class name. Returns {@code false} if either parameter is {@code null}.

- * - *

Example Usage

- *
{@code
-     * AnnotationMirror annotationMirror = ...; // obtain an AnnotationMirror instance
-     * String annotationClassName = "com.example.MyAnnotation";
-     *
-     * // Check if both are non-null and the types match
-     * boolean isMatch = AnnotationUtils.matchesAnnotationTypeName(annotationMirror, annotationClassName);
-     *
-     * // Handle cases where the annotation mirror might be null
-     * isMatch = AnnotationUtils.matchesAnnotationTypeName(null, annotationClassName); // returns false
-     *
-     * // Handle cases where the annotation class name might be null
-     * isMatch = AnnotationUtils.matchesAnnotationTypeName(annotationMirror, null); // returns false
-     * }
- * - * @param annotationMirror the annotation mirror to compare; may be {@code null} - * @param annotationTypeName the target annotation class name to match against; may be {@code null} - * @return {@code true} if both parameters are non-null and their types match by name; - * {@code false} otherwise - */ - static boolean matchesAnnotationTypeName(AnnotationMirror annotationMirror, CharSequence annotationTypeName) { - if (annotationMirror == null || annotationTypeName == null) { - return false; - } - return isSameType(annotationMirror.getAnnotationType(), annotationTypeName); - } - - /** - * Retrieves the name of the attribute method from the given {@link ExecutableElement}. - * - *

This method is typically used to extract the attribute name from an annotation method declaration. - * The returned name corresponds to the method's simple name as defined in the annotation interface.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value();
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attribute names from methods
-     * ExecutableElement valueMethod = ...; // method representing 'value()'
-     * ExecutableElement priorityMethod = ...; // method representing 'priority()'
-     *
-     * String valueName = AnnotationUtils.getAttributeName(valueMethod); // returns "value"
-     * String priorityName = AnnotationUtils.getAttributeName(priorityMethod); // returns "priority"
-     *
-     * // Handle cases where the executable element is null
-     * String name = AnnotationUtils.getAttributeName(null); // returns null
-     * }
- * - * @param attributeMethod the executable element representing the annotation attribute method, may be {@code null} - * @return the name of the attribute method, or {@code null} if the provided element is {@code null} - */ - static String getAttributeName(ExecutableElement attributeMethod) { - return getMethodName(attributeMethod); - } - - /** - * Checks if the provided executable element represents an annotation attribute method - * with the specified name. - * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value();
-     *     int priority() default 0;
-     * }
-     *
-     * // Check if the executable element corresponds to the "value" method
-     * ExecutableElement valueMethod = ...; // method representing 'value()'
-     * boolean isValueMethod = AnnotationUtils.matchesAttributeMethod(valueMethod, "value"); // returns true
-     *
-     * // Check if the executable element corresponds to the "priority" method
-     * ExecutableElement priorityMethod = ...; // method representing 'priority()'
-     * boolean isPriorityMethod = AnnotationUtils.matchesAttributeMethod(priorityMethod, "priority"); // returns true
-     *
-     * // Handle cases where the executable element is null
-     * boolean result = AnnotationUtils.matchesAttributeMethod(null, "value"); // returns false
-     *
-     * // Handle cases where the attribute name is null or blank
-     * result = AnnotationUtils.matchesAttributeMethod(valueMethod, null); // returns false
-     * result = AnnotationUtils.matchesAttributeMethod(valueMethod, ""); // returns false
-     * }
- * - * @param attributeMethod the executable element to check, may be {@code null} - * @param attributeName the expected name of the attribute method, may be {@code null} or blank - * @return {@code true} if the method is not null and its name matches the given attribute name; - * {@code false} otherwise - */ - static boolean matchesAttributeMethod(ExecutableElement attributeMethod, String attributeName) { - return attributeMethod != null && Objects.equals(getAttributeName(attributeMethod), attributeName); - } - - /** - * Checks if two given {@link AnnotationValue} instances are equal by comparing their values. - * - *

This method performs a deep comparison of the values contained within the annotation values. - * If both values are {@code null}, they are considered equal. If only one is {@code null}, they are not equal. - * Otherwise, the method compares the actual values using {@link Objects#equals(Object, Object)}.

- * - *

Example Usage

- *
{@code
-     * AnnotationValue value1 = ...; // obtain an AnnotationValue instance
-     * AnnotationValue value2 = ...; // obtain another AnnotationValue instance
-     *
-     * // Check if both annotation values are equal
-     * boolean isEqual = AnnotationUtils.matchesAttributeValue(value1, value2);
-     *
-     * // Handle cases where either value is null
-     * isEqual = AnnotationUtils.matchesAttributeValue(null, value2); // returns false
-     * isEqual = AnnotationUtils.matchesAttributeValue(value1, null); // returns false
-     * }
- * - * @param one the first annotation value to compare; may be {@code null} - * @param another the second annotation value to compare; may be {@code null} - * @return {@code true} if both annotation values are either {@code null} or their contents are equal; - * {@code false} otherwise - */ - static boolean matchesAttributeValue(AnnotationValue one, AnnotationValue another) { - if (one == another) { - return true; - } - if (one == null || another == null) { - return false; - } - Object oneValue = one.getValue(); - Object anotherValue = another.getValue(); - return Objects.equals(oneValue, anotherValue); - } - - /** - * Checks if the value of the given {@link AnnotationValue} matches the specified attribute value. - * - *

This method compares the actual value extracted from the annotation value with the provided - * attribute value. Returns {@code false} if either parameter is {@code null} or if the values do not match.

- * - *

Example Usage

- *
{@code
-     * AnnotationValue annotationValue = ...; // obtain an AnnotationValue instance
-     * String expectedValue = "example";
-     * boolean isMatch = AnnotationUtils.matchesAttributeValue(annotationValue, expectedValue); // returns true if values match
-     *
-     * // Handle cases where the annotation value is null
-     * boolean result = AnnotationUtils.matchesAttributeValue(null, expectedValue); // returns false
-     *
-     * // Handle cases where the attribute value is null
-     * result = AnnotationUtils.matchesAttributeValue(annotationValue, null); // returns false
-     * }
- * - * @param annotationValue the annotation value to compare; may be {@code null} - * @param attributeValue the target value to match against; may be {@code null} - * @return {@code true} if both parameters are non-null and their values match; - * {@code false} otherwise - */ - static boolean matchesAttributeValue(AnnotationValue annotationValue, Object attributeValue) { - return annotationValue != null && Objects.equals(annotationValue.getValue(), attributeValue); - } - - /** - * Checks if the provided annotation value matches the default value of the specified attribute method. - * - *

This method is useful when determining if an attribute value in an annotation is explicitly set or - * if it falls back to the default value declared in the annotation interface.

- * - *

Example Usage

- *
{@code
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     * }
-     *
-     * AnnotationMirror annotation = ...; // obtained from an annotated element
-     * ExecutableElement attributeMethod = getAttributeMethod(annotation, "value");
-     * AnnotationValue annotationValue = getAttributeValue(annotation, "value");
-     *
-     * boolean isDefaultValue = matchesDefaultAttributeValue(attributeMethod, annotationValue);
-     * }

- * - * @param attributeMethod the executable element representing the annotation attribute method, may be {@code null} - * @param annotationValue the annotation value to compare against the default, may be {@code null} - * @return {@code true} if both the attribute method and annotation value are non-null and the value matches the default; - * {@code false} otherwise - */ - static boolean matchesDefaultAttributeValue(ExecutableElement attributeMethod, AnnotationValue annotationValue) { - return attributeMethod != null && matchesAttributeValue(attributeMethod.getDefaultValue(), annotationValue); - } - - /** - * Retrieves a map of attribute names to their corresponding values from the first matching annotation of the specified class - * on the given annotated construct. This includes both explicitly set values and default values for attributes. - * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attributes from an annotated class
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * Map attributes = AnnotationUtils.getAttributesMap(typeElement, MyAnnotation.class);
-     *
-     * // Example output if MyAnnotation is applied with value="custom"
-     * // attributes will contain:
-     * // {
-     * //     "value" = "custom",
-     * //     "priority" = 0
-     * // }
-     *
-     * // Handle cases where the annotated construct is null
-     * attributes = AnnotationUtils.getAttributesMap(null, MyAnnotation.class); // returns empty map
-     *
-     * // Handle cases where the annotation class is null
-     * attributes = AnnotationUtils.getAttributesMap(typeElement, null); // returns empty map
-     * }
- * - * @param annotatedConstruct the annotated construct (e.g., a class, method, or field) that may contain the annotation, - * may be {@code null} - * @param annotationClass the annotation class used to locate the annotation on the construct, must not be {@code null} - * @return a non-null immutable map of attribute names to their corresponding values; never {@code null}, - * returns an empty map if no annotation is found, the construct is {@code null}, or the annotation class is {@code null} - */ - @Nonnull - @Immutable - static Map getAttributesMap(AnnotatedConstruct annotatedConstruct, Class annotationClass) { - return getAttributesMap(annotatedConstruct, annotationClass, WITH_DEFAULT); - } - - /** - * Retrieves a map of attribute names to their corresponding values from the first matching annotation of the specified class - * on the given annotated construct. This includes both explicitly set values and default values for attributes if the - * {@code withDefault} flag is set to {@code true}. - * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attributes from an annotated class
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * Map attributes = AnnotationUtils.getAttributesMap(typeElement, MyAnnotation.class, true);
-     *
-     * // Example output if MyAnnotation is applied with value="custom"
-     * // attributes will contain:
-     * // {
-     * //     "value" = "custom",
-     * //     "priority" = 0
-     * // }
-     *
-     * // Handle cases where the annotated construct is null
-     * attributes = AnnotationUtils.getAttributesMap(null, MyAnnotation.class, true); // returns empty map
-     *
-     * // Handle cases where the annotation class is null
-     * attributes = AnnotationUtils.getAttributesMap(typeElement, null, true); // returns empty map
-     * }
- * - * @param annotatedConstruct the annotated construct (e.g., a class, method, or field) that may contain the annotation, - * may be {@code null} - * @param annotationClass the annotation class used to locate the annotation on the construct, must not be {@code null} - * @param withDefault flag indicating whether to include default values for attributes that are not explicitly set; - * if {@code true}, default values will be included where applicable - * @return a non-null immutable map of attribute names to their corresponding values; never {@code null}, - * returns an empty map if no annotation is found, the construct is {@code null}, or the annotation class is {@code null} - */ - @Nonnull - @Immutable - static Map getAttributesMap(AnnotatedConstruct annotatedConstruct, Class annotationClass, boolean withDefault) { - return getAttributesMap(getAnnotation(annotatedConstruct, annotationClass), withDefault); - } - - /** - * Retrieves a map of attribute names to their corresponding values from the specified annotation. - * This includes both explicitly set values and default values for attributes. - * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attributes from an annotation instance
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * Map attributes = AnnotationUtils.getAttributesMap(annotation);
-     *
-     * // Example output if MyAnnotation is applied with value="custom"
-     * // attributes will contain:
-     * // {
-     * //     "value" = "custom",
-     * //     "priority" = 0
-     * // }
-     *
-     * // Handle cases where the annotation is null
-     * Map emptyMap = AnnotationUtils.getAttributesMap(null); // returns empty map
-     * }
- * - * @param annotation the specified annotation, may be {@code null} - * @return a non-null immutable map of attribute names to their corresponding values; never {@code null}, - * returns an empty map if the annotation is {@code null} - */ - @Nonnull - @Immutable - static Map getAttributesMap(AnnotationMirror annotation) { - return getAttributesMap(annotation, WITH_DEFAULT); - } - - /** - * Retrieves a map of attribute names to their corresponding values from the specified annotation. - * This includes both explicitly set values and default values for attributes if the - * {@code withDefault} flag is set to {@code true}. - * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attributes from an annotation instance with default values
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * Map attributes = AnnotationUtils.getAttributesMap(annotation, true);
-     *
-     * // Example output if MyAnnotation is applied with value="custom"
-     * // attributes will contain:
-     * // {
-     * //     "value" = "custom",
-     * //     "priority" = 0
-     * // }
-     *
-     * // Retrieve attributes without including default values
-     * Map explicitAttributes = AnnotationUtils.getAttributesMap(annotation, false);
-     *
-     * // Handle cases where the annotation is null
-     * Map emptyMap = AnnotationUtils.getAttributesMap(null, true); // returns empty map
-     * }
- * - * @param annotation the specified annotation, may be {@code null} - * @param withDefault flag indicating whether to include default values for attributes that are not explicitly set; - * if {@code true}, default values will be included where applicable - * @return a non-null immutable map of attribute names to their corresponding values; never {@code null}, - * returns an empty map if the annotation is {@code null} - */ - @Nonnull - @Immutable - static Map getAttributesMap(AnnotationMirror annotation, boolean withDefault) { - Map attributes = getElementValues(annotation, withDefault); - int size = attributes.size(); - if (size < 1) { - return emptyMap(); - } - Map attributesMap = newFixedLinkedHashMap(size); - for (Entry entry : attributes.entrySet()) { - ExecutableElement attributeMethod = entry.getKey(); - String attributeName = getAttributeName(attributeMethod); - Object attributeValue = getAttribute(entry); - attributesMap.put(attributeName, attributeValue); - } - return unmodifiableMap(attributesMap); - } - - /** - * Retrieves the map of annotation attribute methods to their corresponding values from the specified - * {@link AnnotatedConstruct} and annotation class. This method finds the first matching annotation on the construct - * and returns all its declared attribute values, including default values. - * - *

This method is a convenience overload that defaults to including default values. If the annotated construct - * is {@code null} or the annotation class is not present, an empty map is returned. - * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attribute values from an annotated class
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * Map attributes = AnnotationUtils.getElementValues(typeElement, MyAnnotation.class);
-     *
-     * // Example output if MyAnnotation is applied with value="custom"
-     * // attributes will contain:
-     * // {
-     * //     value(): "custom",
-     * //     priority(): 0
-     * // }
-     *
-     * // Handle cases where the annotated construct is null
-     * Map emptyMap = AnnotationUtils.getElementValues(null, MyAnnotation.class); // returns empty map
-     *
-     * // Handle cases where the annotation class is null
-     * emptyMap = AnnotationUtils.getElementValues(typeElement, null); // returns empty map
-     * }
- * - * @param annotatedConstruct the annotated construct (e.g., a class, method, or field) that may contain the annotation, - * may be {@code null} - * @param annotationClass the annotation class used to locate the annotation on the construct, must not be {@code null} - * @return a non-null immutable map of executable elements (attribute methods) to their annotation values; never {@code null}, - * returns an empty map if no annotation is found or if the construct is {@code null} - */ - @Nonnull - @Immutable - static Map getElementValues(AnnotatedConstruct annotatedConstruct, Class annotationClass) { - return getElementValues(annotatedConstruct, annotationClass, WITH_DEFAULT); - } - - /** - * Retrieves the map of annotation attribute methods to their corresponding values from the specified - * {@link AnnotatedConstruct} and annotation class. This method finds the first matching annotation on the construct - * and returns all its declared attribute values, including default values if enabled. - * - *

If the annotated construct is {@code null} or the annotation class is not present, an empty map is returned. - * If the {@code withDefault} flag is set to {@code true}, this method includes default values for attributes - * that are not explicitly set.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attribute values from an annotated class with default values
-     * TypeElement typeElement = ...; // obtain a TypeElement
-     * Map attributes = AnnotationUtils.getElementValues(typeElement, MyAnnotation.class, true);
-     *
-     * // Example output if MyAnnotation is applied with value="custom"
-     * // attributes will contain:
-     * // {
-     * //     value(): "custom",
-     * //     priority(): 0
-     * // }
-     *
-     * // Retrieve attribute values without including default values
-     * Map explicitAttributes = AnnotationUtils.getElementValues(typeElement, MyAnnotation.class, false);
-     *
-     * // Handle cases where the annotated construct is null
-     * Map emptyMap = AnnotationUtils.getElementValues(null, MyAnnotation.class, true); // returns empty map
-     *
-     * // Handle cases where the annotation class is null
-     * emptyMap = AnnotationUtils.getElementValues(typeElement, null, true); // returns empty map
-     * }
- * - * @param annotatedConstruct the annotated construct (e.g., a class, method, or field) that may contain the annotation, - * may be {@code null} - * @param annotationClass the annotation class used to locate the annotation on the construct, must not be {@code null} - * @param withDefault flag indicating whether to include default values for attributes that are not explicitly set; - * if {@code true}, default values will be included where applicable - * @return a non-null immutable map of executable elements (attribute methods) to their annotation values; never {@code null}, - * returns an empty map if no annotation is found or if the construct is {@code null} - */ - @Nonnull - @Immutable - static Map getElementValues(AnnotatedConstruct annotatedConstruct, Class annotationClass, boolean withDefault) { - return getElementValues(getAnnotation(annotatedConstruct, annotationClass), withDefault); - } - - /** - * Retrieves a map of annotation attribute methods to their corresponding values from the specified - * {@link AnnotationMirror}. This method includes both explicitly set values and default values for attributes. - * - *

If the provided annotation is {@code null}, an empty map is returned.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attribute values from an annotation instance
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * Map attributes = AnnotationUtils.getElementValues(annotation);
-     *
-     * // Example output if MyAnnotation is applied with value="custom"
-     * // attributes will contain:
-     * // {
-     * //     value(): "custom",
-     * //     priority(): 0
-     * // }
-     *
-     * // Handle cases where the annotation is null
-     * Map emptyMap = AnnotationUtils.getElementValues(null); // returns empty map
-     * }
- * - * @param annotation the annotation mirror to extract attribute values from, may be {@code null} - * @return a non-null immutable map of executable elements (attribute methods) to their annotation values; - * never {@code null}, returns an empty map if the annotation is {@code null} - */ - @Nonnull - @Immutable - static Map getElementValues(AnnotationMirror annotation) { - return getElementValues(annotation, WITH_DEFAULT); - } - - /** - * Retrieves a map of annotation attribute methods to their corresponding values from the specified - * {@link AnnotationMirror}. This method includes both explicitly set values and default values for attributes - * if the {@code withDefault} flag is set to {@code true}. - * - *

If the provided annotation is {@code null}, an empty map is returned.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve attribute values from an annotation instance with default values
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * Map attributes = AnnotationUtils.getElementValues(annotation, true);
-     *
-     * // Example output if MyAnnotation is applied with value="custom"
-     * // attributes will contain:
-     * // {
-     * //     value(): "custom",
-     * //     priority(): 0
-     * // }
-     *
-     * // Retrieve attribute values without including default values
-     * Map explicitAttributes = AnnotationUtils.getElementValues(annotation, false);
-     *
-     * // Handle cases where the annotation is null
-     * Map emptyMap = AnnotationUtils.getElementValues(null, true); // returns empty map
-     * }
- * - * @param annotation the annotation mirror to extract attribute values from, may be {@code null} - * @param withDefault flag indicating whether to include default values for attributes that are not explicitly set; - * if {@code true}, default values will be included where applicable - * @return a non-null immutable map of executable elements (attribute methods) to their annotation values; - * never {@code null}, returns an empty map if the annotation is {@code null} - */ - @Nonnull - @Immutable - static Map getElementValues(AnnotationMirror annotation, boolean withDefault) { - if (annotation == null) { - return emptyMap(); - } - DeclaredType annotationType = annotation.getAnnotationType(); - List attributeMethods = getDeclaredMethods(annotationType); - int size = attributeMethods.size(); - Map attributes = newFixedLinkedHashMap(size); - Map elementValues = annotation.getElementValues(); - for (int i = 0; i < size; i++) { - ExecutableElement attributeMethod = attributeMethods.get(i); - AnnotationValue annotationValue = elementValues.get(attributeMethod); - if (withDefault && annotationValue == null) { - annotationValue = attributeMethod.getDefaultValue(); - } - if (annotationValue != null) { - attributes.put(attributeMethod, annotationValue); - } - } - return unmodifiableMap(attributes); - } - - /** - * Retrieves the attribute method and its corresponding annotation value from the specified annotation - * based on the given attribute name. If no explicit value is found and {@code withDefault} is true, - * it attempts to find and return the default value for the attribute. - * - *

If the provided annotation is null or the attribute name is blank, this method returns null.

- * - *

Example Usage

- *
{@code
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * String attributeName = "value"; // the name of the attribute to retrieve
-     *
-     * // Retrieve an attribute with default value lookup enabled
-     * Map.Entry attributeEntry = AnnotationUtils.getElementValue(annotation, attributeName, true);
-     *
-     * if (attributeEntry != null) {
-     *     ExecutableElement attributeMethod = attributeEntry.getKey();
-     *     AnnotationValue annotationValue = attributeEntry.getValue();
-     *     // process attribute method and value
-     * }
-     *
-     * // Retrieve an attribute without default value lookup
-     * attributeEntry = AnnotationUtils.getElementValue(annotation, attributeName, false);
-     *
-     * // Handle cases where the annotation is null
-     * attributeEntry = AnnotationUtils.getElementValue(null, attributeName, true); // returns null
-     *
-     * // Handle cases where the attribute name is blank or null
-     * attributeEntry = AnnotationUtils.getElementValue(annotation, null, true); // returns null
-     * }
- * - * @param annotation the annotation mirror to extract the attribute value from, may be {@code null} - * @param attributeName the name of the attribute method to look for, may be blank - * @param withDefault flag indicating whether to include the default value if the attribute is not explicitly set; - * if true, the method will attempt to find and return the default value - * @return an entry containing the executable element (attribute method) and its corresponding annotation value; - * returns null if the annotation is null, the attribute name is blank, or the attribute method cannot be found - */ - @Nullable - @Immutable - static Entry getElementValue(AnnotationMirror annotation, String attributeName, boolean withDefault) { - if (annotation == null || isBlank(attributeName)) { - return null; - } - - ExecutableElement attributeMethod = null; - AnnotationValue annotationValue = null; - Map elementValues = annotation.getElementValues(); - for (Entry entry : elementValues.entrySet()) { - attributeMethod = entry.getKey(); - if (matchesAttributeMethod(attributeMethod, attributeName)) { - annotationValue = entry.getValue(); - break; - } - } - - if (withDefault && annotationValue == null) { // not found if the default value is required - DeclaredType annotationType = annotation.getAnnotationType(); - List attributeMethods = findDeclaredMethods(annotationType, method -> !elementValues.containsKey(method)); - int size = attributeMethods.size(); - for (int i = 0; i < size; i++) { - attributeMethod = attributeMethods.get(i); - if (matchesAttributeMethod(attributeMethod, attributeName)) { - annotationValue = attributeMethod.getDefaultValue(); - break; - } - } - } - - return immutableEntry(attributeMethod, annotationValue); - } - - /** - * Retrieves the attribute method and its corresponding annotation value from the specified element values map - * based on the given attribute name. - * - *

This method searches through the provided map of executable elements (attribute methods) to their annotation - * values to find a matching attribute by name. If no match is found, it returns {@code null}.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Assume elementValues contains:
-     * // {
-     * //     value(): "custom",
-     * //     priority(): 5
-     * // }
-     *
-     * // Retrieve an existing attribute
-     * Entry entry = AnnotationUtils.getElementValue(elementValues, "value");
-     * if (entry != null) {
-     *     String value = (String) entry.getValue().getValue(); // returns "custom"
-     * }
-     *
-     * // Retrieve a non-existent attribute
-     * entry = AnnotationUtils.getElementValue(elementValues, "nonExistent"); // returns null
-     *
-     * // Handle cases where the element values map is null or empty
-     * entry = AnnotationUtils.getElementValue(null, "value"); // returns null
-     * }
- * - * @param elementValues the map of executable elements (attribute methods) to their annotation values; - * may be {@code null} or empty - * @param attributeName the name of the attribute method to look for; may be {@code null} or blank - * @return an entry containing the executable element (attribute method) and its corresponding annotation value; - * returns {@code null} if the element values map is empty, the attribute name is blank, or no matching - * attribute is found - */ - @Nullable - static Entry getElementValue(Map elementValues, String attributeName) { - if (isEmpty(elementValues)) { - return null; - } - for (Entry entry : elementValues.entrySet()) { - if (matchesAttributeMethod(entry.getKey(), attributeName)) { - return entry; - } - } - return null; - } - - /** - * Retrieves the value of the specified attribute from the given annotation. - * - *

This method attempts to find the attribute by name in the provided annotation. If the attribute is not explicitly set, - * it will attempt to retrieve the default value associated with that attribute. If the attribute cannot be resolved or no value is found, - * this method returns {@code null}.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve the value of the "value" attribute
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * String value = AnnotationUtils.getAttribute(annotation, "value"); // returns "default" or explicit value
-     *
-     * // Retrieve the value of the "priority" attribute
-     * int priority = AnnotationUtils.getAttribute(annotation, "priority"); // returns 0 or explicit value
-     *
-     * // Handle cases where the annotation is null
-     * String result = AnnotationUtils.getAttribute(null, "value"); // returns null
-     *
-     * // Handle cases where the attribute name is blank or null
-     * result = AnnotationUtils.getAttribute(annotation, null); // returns null
-     * }
- * - * @param the type of the attribute value to return - * @param annotation the annotation mirror to extract the attribute value from; may be {@code null} - * @param attributeName the name of the attribute method to look for; may be blank or {@code null} - * @return the value of the specified attribute if found, or the default value if available; - * returns {@code null} if the annotation is {@code null}, the attribute name is blank, - * or the attribute cannot be resolved - */ - @Nullable - static T getAttribute(AnnotationMirror annotation, String attributeName) { - return getAttribute(annotation, attributeName, WITH_DEFAULT); - } - - /** - * Retrieves the value of the specified attribute from the given annotation, optionally including the default value. - * - *

This method attempts to find the attribute by name in the provided annotation. If the attribute is not explicitly set, - * and the {@code withDefault} flag is set to {@code true}, it will attempt to retrieve the default value associated - * with that attribute. If the attribute cannot be resolved or no value is found, this method returns {@code null}.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Retrieve the value of the "value" attribute including default
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * String value = AnnotationUtils.getAttribute(annotation, "value", true); // returns "default" or explicit value
-     *
-     * // Retrieve the value of the "priority" attribute without default lookup
-     * int priority = AnnotationUtils.getAttribute(annotation, "priority", false); // returns 0 only if explicitly set
-     *
-     * // Handle cases where the annotation is null
-     * String result = AnnotationUtils.getAttribute(null, "value", true); // returns null
-     *
-     * // Handle cases where the attribute name is blank or null
-     * result = AnnotationUtils.getAttribute(annotation, null, true); // returns null
-     * }
- * - * @param the type of the attribute value to return - * @param annotation the annotation mirror to extract the attribute value from; may be {@code null} - * @param attributeName the name of the attribute method to look for; may be blank or {@code null} - * @param withDefault flag indicating whether to include the default value if the attribute is not explicitly set; - * if {@code true}, the method will attempt to find and return the default value - * @return the value of the specified attribute if found, or the default value if available; - * returns {@code null} if the annotation is {@code null}, the attribute name is blank, - * or the attribute cannot be resolved - */ - @Nullable - static T getAttribute(AnnotationMirror annotation, String attributeName, boolean withDefault) { - Entry attributeEntry = getElementValue(annotation, attributeName, withDefault); - return getAttribute(attributeEntry); - } - - /** - * Retrieves the value of the specified attribute from the provided entry containing the attribute method - * and its corresponding annotation value. - * - *

If the entry is null or either the attribute method or annotation value is unresolved, this method returns {@code null}. - * Otherwise, it delegates to the default {@link AnnotationValueVisitor} to extract the attribute value.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     *     int priority() default 0;
-     * }
-     *
-     * // Assume elementValue contains the entry for "value" attribute
-     * Entry elementValue = ...; // obtain from AnnotationUtils.getElementValue()
-     * String value = AnnotationUtils.getAttribute(elementValue); // returns "default" or explicit value
-     *
-     * // Handle cases where the element value entry is null
-     * String result = AnnotationUtils.getAttribute(null); // returns null
-     * }
- * - * @param the expected type of the attribute value - * @param elementValue an entry containing the attribute method and its corresponding annotation value; - * may be {@code null} - * @return the resolved value of the attribute if found; returns {@code null} if the entry is null, - * or if either the attribute method or annotation value is unresolved - */ - @Nullable - static T getAttribute(Entry elementValue) { - if (elementValue == null) { - return null; - } - - ExecutableElement attributeMethod = elementValue.getKey(); - AnnotationValue annotationValue = elementValue.getValue(); - - return (T) annotationValue.accept(DEFAULT_ANNOTATION_VALUE_VISITOR, attributeMethod); - } - - /** - * Retrieves the value of the default attribute method named {@code "value"} from the specified annotation. - * - *

This method delegates to {@link #getAttribute(AnnotationMirror, String)} to obtain the value of the annotation's - * {@code value()} method. Returns {@code null} if the annotation is {@code null} or if the value cannot be resolved.

- * - *

Example Usage

- *
{@code
-     * // Given an annotation interface:
-     * public @interface MyAnnotation {
-     *     String value() default "default";
-     * }
-     *
-     * // Retrieve the "value" attribute from an annotation instance
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * String value = AnnotationUtils.getValue(annotation); // returns "default" or explicit value
-     *
-     * // Handle cases where the annotation is null
-     * String result = AnnotationUtils.getValue(null); // returns null
-     * }
- * - * @param the expected type of the attribute value - * @param annotation the annotation mirror to extract the value from; may be {@code null} - * @return the resolved value of the annotation's {@code value()} method if found; - * returns {@code null} if the annotation is {@code null} or the value cannot be resolved - */ - @Nullable - static T getValue(AnnotationMirror annotation) { - return getAttribute(annotation, VALUE_ATTRIBUTE_NAME); - } - - /** - * Retrieves the {@link ElementType} array from the specified annotation. - * - * @param annotation the specified annotation, may be {@code null} - * @return a non-null array of {@link ElementType}; never {@code null}, returns an empty array if the annotation is {@code null} - */ - /** - * Retrieves the {@link ElementType} array from the specified annotation. - * - *

This method checks the annotation's type for the presence of a {@link Target} annotation, - * which defines the element types the annotation can be applied to. If the provided annotation - * is {@code null}, this method returns an empty array.

- * - *

Example Usage

- *
{@code
-     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
-     * ElementType[] elementTypes = AnnotationUtils.getElementTypes(annotation);
-     *
-     * // Handle cases where the annotation is null
-     * ElementType[] emptyTypes = AnnotationUtils.getElementTypes(null); // returns empty array
-     * }
- * - * @param annotation the specified annotation, may be {@code null} - * @return a non-null array of {@link ElementType}; never {@code null}, returns an empty array if the annotation is {@code null} - */ - @Nonnull - static ElementType[] getElementTypes(AnnotationMirror annotation) { - return annotation == null ? EMPTY_ELEMENT_TYPE_ARRAY : getElementTypes(annotation.getAnnotationType()); - } - - /** - * Retrieves the {@link ElementType} array from the specified annotation type by checking - * the {@link Target} annotation associated with it. If the annotation type does not have a - * {@link Target} annotation, an empty array is returned. - * - *

Example Usage

- *
{@code
-     * // Given an annotation type:
-     * public @interface MyAnnotation {
-     * }
-     *
-     * DeclaredType annotationType = ...; // obtain a DeclaredType for MyAnnotation
-     * ElementType[] elementTypes = AnnotationUtils.getElementTypes(annotationType);
-     *
-     * // If MyAnnotation is annotated with @Target(ElementType.TYPE)
-     * // elementTypes will contain: { ElementType.TYPE }
-     *
-     * // Handle cases where the annotation type does not have a @Target annotation
-     * ElementType[] emptyTypes = AnnotationUtils.getElementTypes(annotationType); // returns empty array
-     * }
- * - * @param annotationType the declared type of the annotation - * @return a non-null immutable array of {@link ElementType}; never {@code null}, - * returns an empty array if no {@link Target} annotation is present - */ - @Nonnull - static ElementType[] getElementTypes(DeclaredType annotationType) { - AnnotationMirror targetAnnotation = findAnnotation(annotationType, Target.class); - ElementType[] elementTypes = getValue(targetAnnotation); - return elementTypes == null ? EMPTY_ELEMENT_TYPE_ARRAY : elementTypes; - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ClassUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ClassUtils.java deleted file mode 100644 index 77988604c..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ClassUtils.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.util; - -import io.microsphere.util.Utils; - -import javax.lang.model.type.TypeMirror; - -import static io.microsphere.annotation.processor.util.TypeUtils.ofTypeElement; -import static io.microsphere.constants.SymbolConstants.DOLLAR_CHAR; -import static io.microsphere.constants.SymbolConstants.DOT_CHAR; -import static io.microsphere.util.ClassLoaderUtils.getClassLoader; -import static io.microsphere.util.ClassLoaderUtils.resolveClass; - -/** - * The utilities class for {@link Class} - * - * @author Mercy - * @see Class - * @since 1.0.0 - */ -public interface ClassUtils extends Utils { - - /** - * Gets the fully qualified class name from the given {@link TypeMirror}. - * - *

- * This method is useful when working with annotation processors or other - * compile-time code analysis tools that deal with type information represented - * as a {@link TypeMirror}. - *

- * - *

Example Usage

- *
-     * TypeMirror type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass").asType();
-     * String className = getClassName(type); // returns "com.example.MyClass"
-     * 
- * - * @param type the type mirror to extract the class name from - * @return the fully qualified class name as a String - */ - static String getClassName(TypeMirror type) { - return ofTypeElement(type).getQualifiedName().toString(); - } - - /** - * Loads the class represented by the given {@link TypeMirror}. - * - *

This method derives the fully qualified class name from the type mirror and - * delegates to the other {@link #loadClass(String)} method for class loading. - * It is particularly useful in annotation processors or compile-time tools where - * types are primarily accessed via their mirror representations. - * - *

Example Usage

- *
-     * TypeMirror type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass").asType();
-     * Class clazz = ClassUtils.loadClass(type); // loads com.example.MyClass
-     * 
- * - * @param type the type mirror representing the class to load - * @return the resolved {@link Class}, or {@code null} if the class cannot be found - */ - static Class loadClass(TypeMirror type) { - return loadClass(getClassName(type)); - } - - /** - * Loads the class represented by the given fully qualified class name. - * - *

This method attempts to resolve the class using the provided class name and the class loader - * obtained from {@link ClassUtils}. If the class is not found, an attempt is made to resolve it - * as a nested or inner class by replacing the last dot ({@code .}) with a dollar sign ({@code $}). - * - *

Example Usage

- *
-     * Class clazz = ClassUtils.loadClass("com.example.MyClass");
-     * // If MyClass is an inner class, this may also attempt to load "com.example.My$Class"
-     * 
- * - * @param className the fully qualified name of the class to load - * @return the resolved {@link Class}, or {@code null} if the class cannot be found - */ - static Class loadClass(String className) { - ClassLoader classLoader = getClassLoader(ClassUtils.class); - Class klass = resolveClass(className, classLoader); - if (klass == null) { - int index = className.lastIndexOf(DOT_CHAR); - if (index > 0) { - className = className.substring(0, index) + DOLLAR_CHAR + className.substring(index + 1); - } - } - return resolveClass(className, classLoader); - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ConstructorUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ConstructorUtils.java deleted file mode 100644 index e19f43845..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ConstructorUtils.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.Immutable; -import io.microsphere.annotation.Nonnull; -import io.microsphere.annotation.Nullable; -import io.microsphere.util.Utils; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeMirror; -import java.lang.reflect.Constructor; -import java.lang.reflect.Type; -import java.util.List; -import java.util.function.Predicate; - -import static io.microsphere.annotation.processor.util.ElementUtils.filterElements; -import static io.microsphere.annotation.processor.util.ElementUtils.matchParameterTypes; -import static io.microsphere.annotation.processor.util.MemberUtils.getDeclaredMembers; -import static io.microsphere.collection.CollectionUtils.isEmpty; -import static io.microsphere.collection.ListUtils.first; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; -import static java.util.Collections.emptyList; -import static javax.lang.model.util.ElementFilter.constructorsIn; - -/** - * The utils class for {@link Constructor constructor} - * - * @author Mercy - * @see Constructor - * @see ExecutableElement - * @see ElementKind#CONSTRUCTOR - * @since 1.0.0 - */ -public interface ConstructorUtils extends Utils { - - /** - * Retrieves the list of declared constructors from the provided {@link TypeElement}. - *

- * This method provides a null-safe way to obtain the constructors of a given type. - * If the input {@code type} is {@code null}, an empty list is returned. - *

- * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * List constructors = getDeclaredConstructors(typeElement);
-     * if (!constructors.isEmpty()) {
-     *     for (ExecutableElement constructor : constructors) {
-     *         System.out.println("Constructor: " + constructor);
-     *     }
-     * } else {
-     *     System.out.println("No constructors found.");
-     * }
-     * }
- * - *

- * This method is particularly useful when processing annotations during compilation, - * where it is necessary to inspect the constructors of a class without throwing exceptions - * for null inputs. - *

- * - * @param type the {@link TypeElement} representing the type to retrieve constructors from - * @return a {@link List} of {@link ExecutableElement} objects representing the declared constructors; - * never {@code null}, but may be empty if no constructors are found or if the input is {@code null} - */ - @Nonnull - @Immutable - static List getDeclaredConstructors(TypeElement type) { - return type == null ? emptyList() : getDeclaredConstructors(type.asType()); - } - - /** - * Retrieves the list of declared constructors from the provided {@link TypeMirror}. - *

- * This method provides a null-safe way to obtain the constructors of a given type. - * If the input {@code type} is {@code null}, an empty list is returned. - *

- * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * List constructors = getDeclaredConstructors(typeMirror);
-     * if (!constructors.isEmpty()) {
-     *     for (ExecutableElement constructor : constructors) {
-     *         System.out.println("Constructor: " + constructor);
-     *     }
-     * } else {
-     *     System.out.println("No constructors found.");
-     * }
-     * }
- * - *

- * This method is particularly useful when processing annotations during compilation, - * where it is necessary to inspect the constructors of a class without throwing exceptions - * for null inputs. - *

- * - * @param type the {@link TypeMirror} representing the type to retrieve constructors from - * @return a {@link List} of {@link ExecutableElement} objects representing the declared constructors; - * never {@code null}, but may be empty if no constructors are found or if the input is {@code null} - */ - @Nonnull - @Immutable - static List getDeclaredConstructors(TypeMirror type) { - return type == null ? emptyList() : findDeclaredConstructors(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Finds a declared constructor in the specified {@link TypeElement} that matches the given parameter types. - *

- * This method provides a null-safe way to locate a constructor based on its parameter types. - * If the input {@code type} is {@code null}, or no matching constructor is found, {@code null} is returned. - *

- * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * Type[] paramTypes = new Type[] { String.class, int.class };
-     * ExecutableElement constructor = findConstructor(typeElement, paramTypes);
-     * if (constructor != null) {
-     *     System.out.println("Found constructor: " + constructor);
-     * } else {
-     *     System.out.println("Constructor not found.");
-     * }
-     * }
- * - * @param type the {@link TypeElement} representing the type to search for constructors - * @param parameterTypes the array of {@link Type} objects representing the parameter types to match - * @return the matched {@link ExecutableElement} representing the constructor; may be {@code null} - */ - @Nullable - static ExecutableElement findConstructor(TypeElement type, Type... parameterTypes) { - return type == null ? null : findConstructor(type.asType(), parameterTypes); - } - - /** - * Finds a declared constructor in the specified {@link TypeMirror} that matches the given parameter types. - *

- * This method provides a null-safe way to locate a constructor based on its parameter types. - * If the input {@code type} is {@code null}, or no matching constructor is found, {@code null} is returned. - *

- * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * Type[] paramTypes = new Type[] { String.class, int.class };
-     * ExecutableElement constructor = findConstructor(typeMirror, paramTypes);
-     * if (constructor != null) {
-     *     System.out.println("Found constructor: " + constructor);
-     * } else {
-     *     System.out.println("Constructor not found.");
-     * }
-     * }
- * - * @param type the {@link TypeMirror} representing the type to search for constructors - * @param parameterTypes the array of {@link Type} objects representing the parameter types to match - * @return the matched {@link ExecutableElement} representing the constructor; may be {@code null} - */ - @Nullable - static ExecutableElement findConstructor(TypeMirror type, Type... parameterTypes) { - if (type == null) { - return null; - } - return first(findDeclaredConstructors(type, constructor -> matchParameterTypes(constructor, parameterTypes))); - } - - /** - * Retrieves and filters the list of declared constructors from the provided {@link TypeElement}. - *

- * This method provides a null-safe way to obtain the constructors of a given type. - * If the input {@code type} is {@code null}, an empty list is returned. - * The provided filters can be used to selectively include only those constructors that match the criteria. - *

- * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * // Get all declared constructors
-     * List allConstructors = findDeclaredConstructors(typeElement);
-     *
-     * // Get only constructors with specific parameter types
-     * List filteredConstructors = findDeclaredConstructors(typeElement,
-     *     constructor -> matchParameterTypes(constructor, String.class, int.class));
-     *
-     * if (!filteredConstructors.isEmpty()) {
-     *     for (ExecutableElement constructor : filteredConstructors) {
-     *         System.out.println("Matching Constructor: " + constructor);
-     *     }
-     * } else {
-     *     System.out.println("No matching constructors found.");
-     * }
-     * }
- * - * @param type the {@link TypeElement} representing the type to retrieve constructors from - * @param constructorFilters optional predicates to filter the constructors; if none are provided, all constructors are included - * @return a {@link List} of {@link ExecutableElement} objects representing the filtered declared constructors; - * never {@code null}, but may be empty if no matching constructors are found or if the input is {@code null} - */ - @Nonnull - @Immutable - static List findDeclaredConstructors(TypeElement type, Predicate... constructorFilters) { - return type == null ? emptyList() : findDeclaredConstructors(type.asType(), constructorFilters); - } - - /** - * Retrieves and filters the list of declared constructors from the provided {@link TypeMirror}. - *

- * This method provides a null-safe way to obtain the constructors of a given type. - * If the input {@code type} is {@code null}, an empty list is returned. - * The provided filters can be used to selectively include only those constructors that match the criteria. - *

- * - * @param type the {@link TypeMirror} representing the type to retrieve constructors from - * @param constructorFilters optional predicates to filter the constructors; if none are provided, all constructors are included - * @return a {@link List} of {@link ExecutableElement} objects representing the filtered declared constructors; - * never {@code null}, but may be empty if no matching constructors are found or if the input is {@code null} - */ - @Nonnull - @Immutable - static List findDeclaredConstructors(TypeMirror type, Predicate... constructorFilters) { - return filterDeclaredConstructors(type, constructorFilters); - } - - /** - * Filters and retrieves the list of declared constructors from the provided {@link TypeMirror}. - *

- * This method is responsible for extracting all declared constructors from the given type - * and applying the specified filters to narrow down the results. If the input {@code type} - * is {@code null}, an empty list is returned. - *

- * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     *
-     * // Get all declared constructors
-     * List allConstructors = filterDeclaredConstructors(typeMirror);
-     *
-     * // Get only constructors with specific parameter types
-     * List filteredConstructors = filterDeclaredConstructors(typeMirror,
-     *     constructor -> matchParameterTypes(constructor, String.class, int.class));
-     *
-     * if (!filteredConstructors.isEmpty()) {
-     *     for (ExecutableElement constructor : filteredConstructors) {
-     *         System.out.println("Matching Constructor: " + constructor);
-     *     }
-     * } else {
-     *     System.out.println("No matching constructors found.");
-     * }
-     * }
- * - * @param type the {@link TypeMirror} representing the type to retrieve constructors from - * @param constructorFilters optional predicates to filter the constructors; if none are provided, all constructors are included - * @return a {@link List} of {@link ExecutableElement} objects representing the filtered declared constructors; - * never {@code null}, but may be empty if no matching constructors are found or if the input is {@code null} - */ - @Nonnull - @Immutable - static List filterDeclaredConstructors(TypeMirror type, Predicate... constructorFilters) { - if (type == null) { - return emptyList(); - } - - List declaredMembers = getDeclaredMembers(type, false); - if (isEmpty(declaredMembers)) { - return emptyList(); - } - - List constructors = constructorsIn(declaredMembers); - - return filterElements(constructors, constructorFilters); - } - -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ElementUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ElementUtils.java deleted file mode 100644 index a6d7db851..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ElementUtils.java +++ /dev/null @@ -1,734 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.Immutable; -import io.microsphere.annotation.Nonnull; -import io.microsphere.util.Utils; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.VariableElement; -import java.lang.annotation.ElementType; -import java.lang.reflect.Type; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; - -import static io.microsphere.annotation.processor.util.TypeUtils.isSameType; -import static io.microsphere.collection.CollectionUtils.isEmpty; -import static io.microsphere.lang.function.Predicates.and; -import static io.microsphere.reflect.TypeUtils.getTypeNames; -import static io.microsphere.util.ArrayUtils.isNotEmpty; -import static io.microsphere.util.ArrayUtils.length; -import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableList; -import static java.util.stream.Collectors.toList; -import static javax.lang.model.element.ElementKind.CLASS; -import static javax.lang.model.element.ElementKind.OTHER; -import static javax.lang.model.element.ElementKind.valueOf; -import static javax.lang.model.element.Modifier.PUBLIC; -import static javax.lang.model.element.Modifier.STATIC; - -/** - * The utility class for {@link Element} - * - * @author Mercy - * @see Element - * @see Enum - * @since 1.0.0 - */ -public interface ElementUtils extends Utils { - - /** - * Returns {@code true} if the specified {@link ElementKind} represents a class-like element, - * including: - *
    - *
  • {@link ElementKind#CLASS}
  • - *
  • {@link ElementKind#ENUM}
  • - *
  • {@link ElementKind#RECORD}
  • - *
- * - *

This method serves as a convenience wrapper around {@link ElementKind#isClass()}. - * It also guards against null input by returning {@code false} when the argument is null. - * - *

Example Usage

- *
{@code
-     * ElementKind classKind = ElementKind.CLASS;
-     * boolean result = isClass(classKind); // returns true
-     *
-     * ElementKind methodKind = ElementKind.METHOD;
-     * result = isClass(methodKind); // returns false
-     *
-     * result = isClass(null); // returns false
-     * }
- * - * @param kind the ElementKind to check, may be null - * @return {@code true} if the kind is a class-like element, {@code false} otherwise - * @see ElementKind#isClass() - */ - static boolean isClass(ElementKind kind) { - return kind != null && kind.isClass(); - } - - /** - * Returns {@code true} if this is a kind of interface: - * either {@code INTERFACE} or {@code ANNOTATION_TYPE}. - * - *

- * This method serves as a convenience wrapper around {@link ElementKind#isInterface()}. - * It also guards against null input by returning {@code false} when the argument is null. - *

- * - *

Example Usage

- *
{@code
-     * ElementKind interfaceKind = ElementKind.INTERFACE;
-     * boolean result = isInterface(interfaceKind); // returns true
-     *
-     * ElementKind annotationKind = ElementKind.ANNOTATION_TYPE;
-     * result = isInterface(annotationKind); // returns true
-     *
-     * ElementKind classKind = ElementKind.CLASS;
-     * result = isInterface(classKind); // returns false
-     *
-     * result = isInterface(null); // returns false
-     * }
- * - * @param kind the ElementKind to check, may be null - * @return {@code true} if the kind is an interface-like element, {@code false} otherwise - * @see ElementKind#isInterface() - */ - static boolean isInterface(ElementKind kind) { - return kind != null && kind.isInterface(); - } - - /** - * Returns {@code true} if the specified {@link ElementKind} represents a declared type, - * which includes both {@linkplain #isClass(ElementKind) class-like} and - * {@linkplain #isInterface(ElementKind) interface-like} elements. - * - *

This method serves as a convenience wrapper that combines the checks for class and interface kinds. - * It also guards against null input by safely delegating to the respective methods. - * - *

Example Usage

- *
{@code
-     * ElementKind classKind = ElementKind.CLASS;
-     * boolean result = isDeclaredType(classKind); // returns true
-     *
-     * ElementKind interfaceKind = ElementKind.INTERFACE;
-     * result = isDeclaredType(interfaceKind); // returns true
-     *
-     * ElementKind methodKind = ElementKind.METHOD;
-     * result = isDeclaredType(methodKind); // returns false
-     *
-     * result = isDeclaredType(null); // returns false
-     * }
- * - * @param kind the ElementKind to check, may be null - * @return {@code true} if the kind is a declared type, {@code false} otherwise - * @see #isClass(ElementKind) - * @see #isInterface(ElementKind) - */ - static boolean isDeclaredType(ElementKind kind) { - return isClass(kind) || isInterface(kind); - } - - /** - * Returns {@code true} if this is a kind of field: - * either {@code FIELD} or {@code ENUM_CONSTANT}. - * - *

- * This method serves as a convenience wrapper around {@link ElementKind#isField()}. - * It also guards against null input by returning {@code false} when the argument is null. - *

- * - *

Example Usage

- *
{@code
-     * ElementKind fieldKind = ElementKind.FIELD;
-     * boolean result = isField(fieldKind); // returns true
-     *
-     * ElementKind enumConstantKind = ElementKind.ENUM_CONSTANT;
-     * result = isField(enumConstantKind); // returns true
-     *
-     * ElementKind methodKind = ElementKind.METHOD;
-     * result = isField(methodKind); // returns false
-     *
-     * result = isField(null); // returns false
-     * }
- * - * @param kind the ElementKind to check, may be null - * @return {@code true} if the kind is a field-like element, {@code false} otherwise - * @see ElementKind#isField() - */ - static boolean isField(ElementKind kind) { - return kind != null && kind.isField(); - } - - /** - * Returns {@code true} if this is a kind of executable: either - * {@code METHOD}, {@code CONSTRUCTOR}, {@code STATIC_INIT}, or - * {@code INSTANCE_INIT}. - * - *

This method serves as a convenience wrapper around {@link ElementKind#isExecutable()}. - * It also guards against null input by returning {@code false} when the argument is null. - * - *

Example Usage

- *
{@code
-     * ElementKind methodKind = ElementKind.METHOD;
-     * boolean result = isExecutable(methodKind); // returns true
-     *
-     * ElementKind constructorKind = ElementKind.CONSTRUCTOR;
-     * result = isExecutable(constructorKind); // returns true
-     *
-     * ElementKind staticInitKind = ElementKind.STATIC_INIT;
-     * result = isExecutable(staticInitKind); // returns true
-     *
-     * ElementKind instanceInitKind = ElementKind.INSTANCE_INIT;
-     * result = isExecutable(instanceInitKind); // returns true
-     *
-     * ElementKind classKind = ElementKind.CLASS;
-     * result = isExecutable(classKind); // returns false
-     *
-     * result = isExecutable(null); // returns false
-     * }
- * - * @param kind the ElementKind to check, may be null - * @return {@code true} if the kind is an executable-like element, {@code false} otherwise - * @see ElementKind#isExecutable() - */ - static boolean isExecutable(ElementKind kind) { - if (kind != null) { - switch (kind) { - case METHOD: - case CONSTRUCTOR: - case STATIC_INIT: - case INSTANCE_INIT: - return true; - } - } - return false; - } - - /** - * Returns {@code true} if the specified {@link ElementKind} represents a member element, - * which includes both {@linkplain #isField(ElementKind) field-like} and - * {@linkplain #isExecutable(ElementKind) executable-like} elements. - * - *

This method serves as a convenience wrapper that combines the checks for field and executable kinds. - * It also guards against null input by safely delegating to the respective methods. - * - *

Example Usage

- *
{@code
-     * ElementKind fieldKind = ElementKind.FIELD;
-     * boolean result = isMember(fieldKind); // returns true
-     *
-     * ElementKind methodKind = ElementKind.METHOD;
-     * result = isMember(methodKind); // returns true
-     *
-     * ElementKind classKind = ElementKind.CLASS;
-     * result = isMember(classKind); // returns false
-     *
-     * result = isMember(null); // returns false
-     * }
- * - * @param kind the ElementKind to check, may be null - * @return {@code true} if the kind is a member-like element, {@code false} otherwise - * @see #isField(ElementKind) - * @see #isExecutable(ElementKind) - */ - static boolean isMember(ElementKind kind) { - return isField(kind) || isExecutable(kind); - } - - /** - * Returns {@code true} if the specified {@link ElementKind} represents an initializer, - * either {@code STATIC_INIT} or {@code INSTANCE_INIT}. - * - *

- * This method serves as a convenience wrapper around {@link ElementKind#isInitializer()}. - * It also guards against null input by returning {@code false} when the argument is null. - *

- * - *

Example Usage

- *
{@code
-     * ElementKind staticInitKind = ElementKind.STATIC_INIT;
-     * boolean result = isInitializer(staticInitKind); // returns true
-     *
-     * ElementKind instanceInitKind = ElementKind.INSTANCE_INIT;
-     * result = isInitializer(instanceInitKind); // returns true
-     *
-     * ElementKind methodKind = ElementKind.METHOD;
-     * result = isInitializer(methodKind); // returns false
-     *
-     * result = isInitializer(null); // returns false
-     * }
- * - * @param kind the ElementKind to check, may be null - * @return {@code true} if the kind is an initializer-like element, {@code false} otherwise - * @see ElementKind#isInitializer() - */ - static boolean isInitializer(ElementKind kind) { - if (kind != null) { - switch (kind) { - case STATIC_INIT: - case INSTANCE_INIT: - return true; - } - } - return false; - } - - /** - * Returns {@code true} if the specified {@link ElementKind} represents a variable-like element, - * including: - *
    - *
  • {@link ElementKind#ENUM_CONSTANT}
  • - *
  • {@link ElementKind#FIELD}
  • - *
  • {@link ElementKind#PARAMETER}
  • - *
  • {@link ElementKind#LOCAL_VARIABLE}
  • - *
  • {@link ElementKind#EXCEPTION_PARAMETER}
  • - *
  • {@link ElementKind#RESOURCE_VARIABLE}
  • - *
  • {@link ElementKind#BINDING_VARIABLE}
  • - *
- * - *

This method serves as a convenience wrapper around {@link ElementKind#isVariable()}. - * It also guards against null input by returning {@code false} when the argument is null. - * - *

Example Usage

- *
{@code
-     * ElementKind enumConstantKind = ElementKind.ENUM_CONSTANT;
-     * boolean result = isVariable(enumConstantKind); // returns true
-     *
-     * ElementKind fieldKind = ElementKind.FIELD;
-     * result = isVariable(fieldKind); // returns true
-     *
-     * ElementKind parameterKind = ElementKind.PARAMETER;
-     * result = isVariable(parameterKind); // returns true
-     *
-     * ElementKind localVariableKind = ElementKind.LOCAL_VARIABLE;
-     * result = isVariable(localVariableKind); // returns true
-     *
-     * ElementKind exceptionParameterKind = ElementKind.EXCEPTION_PARAMETER;
-     * result = isVariable(exceptionParameterKind); // returns true
-     *
-     * ElementKind resourceVariableKind = ElementKind.RESOURCE_VARIABLE;
-     * result = isVariable(resourceVariableKind); // returns true
-     *
-     * ElementKind bindingVariableKind = ElementKind.BINDING_VARIABLE;
-     * result = isVariable(bindingVariableKind); // returns true
-     *
-     * ElementKind methodKind = ElementKind.METHOD;
-     * result = isVariable(methodKind); // returns false
-     *
-     * result = isVariable(null); // returns false
-     * }
- * - * @param kind the ElementKind to check, may be null - * @return {@code true} if the kind is a variable-like element, {@code false} otherwise - * @see ElementKind#isVariable() - */ - static boolean isVariable(ElementKind kind) { - if (kind != null) { - switch (kind) { - case ENUM_CONSTANT: - case FIELD: - case PARAMETER: - case LOCAL_VARIABLE: - case EXCEPTION_PARAMETER: - case RESOURCE_VARIABLE: - return true; - } - // To be compatible with JDK 16 - return "BINDING_VARIABLE".equals(kind.name()); - } - return false; - } - - /** - * Converts the specified {@link ElementType} to an equivalent {@link ElementKind}. - * - *

- * This method maps the provided {@link ElementType} to a corresponding {@link ElementKind}. - * If the provided {@code elementType} is {@code null}, this method returns {@link ElementKind#OTHER}. - *

- * - *

Example Usage

- *
{@code
-     * ElementType typeElement = ElementType.TYPE;
-     * ElementKind result = toElementKind(typeElement); // returns ElementKind.CLASS
-     *
-     * ElementType typeUseElement = ElementType.TYPE_USE;
-     * result = toElementKind(typeUseElement); // returns ElementKind.CLASS
-     *
-     * ElementType fieldElement = ElementType.FIELD;
-     * result = toElementKind(fieldElement); // returns ElementKind.FIELD
-     *
-     * result = toElementKind(null); // returns ElementKind.OTHER
-     * }
- * - * @param elementType the ElementType to convert, may be {@code null} - * @return the corresponding ElementKind, never {@code null} - */ - static ElementKind toElementKind(ElementType elementType) { - if (elementType == null) { - return OTHER; - } - switch (elementType) { - case TYPE: - case TYPE_USE: - return CLASS; - default: - return valueOf(elementType.name()); - } - } - - /** - * Checks whether the specified {@link ElementKind} matches the specified {@link ElementType}. - * - *

- * This method compares the provided {@link ElementKind} with the result of converting the - * {@link ElementType} to an equivalent {@link ElementKind} using {@link #toElementKind(ElementType)}. - * If either argument is null, the comparison will return {@code false}. - *

- * - *

Example Usage

- *
{@code
-     * ElementKind classKind = ElementKind.CLASS;
-     * ElementType typeElement = ElementType.TYPE;
-     * boolean result = matchesElementType(classKind, typeElement); // returns true
-     *
-     * ElementKind fieldKind = ElementKind.FIELD;
-     * ElementType typeUseElement = ElementType.TYPE_USE;
-     * result = matchesElementType(fieldKind, typeUseElement); // returns false
-     *
-     * result = matchesElementType(null, ElementType.TYPE); // returns false
-     * result = matchesElementType(ElementKind.CLASS, null); // returns false
-     * }
- * - * @param elementKind the ElementKind to check, may be {@code null} - * @param elementType the ElementType to compare against, may be {@code null} - * @return {@code true} if the ElementKind matches the converted ElementType, {@code false} otherwise - * @see #toElementKind(ElementType) - */ - static boolean matchesElementType(ElementKind elementKind, ElementType elementType) { - return elementKind == toElementKind(elementType); - } - - /** - * Checks whether the specified {@link ElementKind} matches any of the specified {@link ElementType}s. - * - *

- * This method converts each {@link ElementType} to its corresponding {@link ElementKind} - * using {@link #toElementKind(ElementType)}, and then compares it with the provided - * {@link ElementKind}. If any match is found, the method returns {@code true}. - *

- * - *

Example Usage

- *
{@code
-     * ElementKind classKind = ElementKind.CLASS;
-     * boolean result = matchesElementType(classKind, ElementType.TYPE, ElementType.METHOD); // returns true
-     *
-     * result = matchesElementType(classKind, ElementType.FIELD, ElementType.CONSTRUCTOR); // returns false
-     *
-     * result = matchesElementType(null, ElementType.TYPE); // returns false
-     *
-     * result = matchesElementType(ElementKind.FIELD, (ElementType[]) null); // returns false
-     * }
- * - * @param elementKind the ElementKind to check, may be {@code null} - * @param elementTypes the array of ElementTypes to match against, may be {@code null} - * @return {@code true} if the ElementKind matches any of the converted ElementTypes, {@code false} otherwise - * @see #toElementKind(ElementType) - * @see #matchesElementType(ElementKind, ElementType) - */ - static boolean matchesElementType(ElementKind elementKind, ElementType... elementTypes) { - int length = length(elementTypes); - if (length < 1) { - return false; - } - for (int i = 0; i < length; i++) { - if (matchesElementType(elementKind, elementTypes[i])) { - return true; - } - } - return false; - } - - /** - * Checks whether the specified {@link Element} matches any of the specified {@link ElementType}s. - * - *

- * This method determines if the provided {@link Element} has a kind that matches any of the - * converted {@link ElementKind} values derived from the given {@link ElementType}s. - * It delegates to {@link #matchesElementType(ElementKind, ElementType...)} for the actual comparison. - *

- * - *

Example Usage

- *
{@code
-     * Element element = ...; // an Element of kind CLASS
-     * boolean result = matchesElementType(element, ElementType.TYPE, ElementType.TYPE_USE); // returns true
-     *
-     * result = matchesElementType(element, ElementType.FIELD, ElementType.METHOD); // returns false
-     *
-     * result = matchesElementType(null, ElementType.TYPE); // returns false
-     *
-     * result = matchesElementType(element, (ElementType[]) null); // returns false
-     * }
- * - * @param element the Element to check, may be {@code null} - * @param elementTypes the array of ElementTypes to match against, may be {@code null} - * @return {@code true} if the element matches any of the converted ElementTypes, {@code false} otherwise - * @see #matchesElementType(ElementKind, ElementType...) - */ - static boolean matchesElementType(Element element, ElementType... elementTypes) { - return element != null && matchesElementType(element.getKind(), elementTypes); - } - - /** - * Checks whether the specified {@link Element} matches the specified {@link ElementKind}. - * - *

- * This method returns {@code false} if either the element or the kind is {@code null}. - * Otherwise, it compares the kind of the element with the provided {@link ElementKind}. - *

- * - *

Example Usage

- *
{@code
-     * Element element = ...; // an Element of kind METHOD
-     * ElementKind methodKind = ElementKind.METHOD;
-     * boolean result = matchesElementKind(element, methodKind); // returns true
-     *
-     * result = matchesElementKind(null, methodKind); // returns false
-     * result = matchesElementKind(element, null); // returns false
-     * }
- * - * @param member the Element to check, may be {@code null} - * @param kind the ElementKind to match, may be {@code null} - * @return {@code true} if the element is not null and its kind matches the specified kind; otherwise, {@code false} - */ - static boolean matchesElementKind(Element member, ElementKind kind) { - return member == null || kind == null ? false : kind.equals(member.getKind()); - } - - /** - * Checks whether the specified {@link Element} is public and non-static. - * - *

This method verifies if the provided element has the {@link Modifier#PUBLIC} modifier - * and does not have the {@link Modifier#STATIC} modifier. - * - *

Example Usage

- *
{@code
-     * Element methodElement = ...; // an Element with PUBLIC and non-STATIC modifiers
-     * boolean result = isPublicNonStatic(methodElement); // returns true
-     *
-     * Element staticMethodElement = ...; // an Element with PUBLIC and STATIC modifiers
-     * result = isPublicNonStatic(staticMethodElement); // returns false
-     *
-     * result = isPublicNonStatic(null); // returns false
-     * }
- * - * @param member the Element to check, may be null - * @return {@code true} if the element is public and non-static; otherwise, {@code false} - */ - static boolean isPublicNonStatic(Element member) { - return hasModifiers(member, PUBLIC) && !hasModifiers(member, STATIC); - } - - /** - * Checks whether the specified {@link Element} has all of the specified {@link Modifier}s. - * - *

- * This method returns {@code false} if the element is {@code null} or if the modifiers array is {@code null}. - * Otherwise, it verifies that the element contains all the provided modifiers. - *

- * - *

Example Usage

- *
{@code
-     * Element methodElement = ...; // an Element with PUBLIC and STATIC modifiers
-     * boolean result = hasModifiers(methodElement, Modifier.PUBLIC, Modifier.STATIC); // returns true
-     *
-     * result = hasModifiers(methodElement, Modifier.PRIVATE); // returns false
-     *
-     * result = hasModifiers(null, Modifier.PUBLIC); // returns false
-     *
-     * result = hasModifiers(methodElement, (Modifier[]) null); // returns false
-     * }
- * - * @param member the {@link Element} to check, may be {@code null} - * @param modifiers the array of {@link Modifier}s to match, may be {@code null} - * @return {@code true} if the element is not null and contains all specified modifiers; otherwise, {@code false} - */ - static boolean hasModifiers(Element member, Modifier... modifiers) { - if (member == null || modifiers == null) { - return false; - } - Set actualModifiers = member.getModifiers(); - for (Modifier modifier : modifiers) { - if (!actualModifiers.contains(modifier)) { - return false; - } - } - return true; - } - - /** - * Filters the provided list of {@link Element} objects based on the given array of {@link Predicate} conditions. - * - *

This method applies a logical AND combination of all provided predicates to filter the elements. - * If the input list is empty or the predicates array is null or empty, an empty list is returned.

- * - *

Example Usage

- *
{@code
-     * List elements = ...; // a list of Elements
-     *
-     * // Filter public and static methods
-     * List filtered = filterElements(elements,
-     *     element -> ElementUtils.hasModifiers(element, Modifier.PUBLIC),
-     *     element -> ElementUtils.hasModifiers(element, Modifier.STATIC)
-     * );
-     *
-     * // Returns an empty list if no elements match
-     * filtered = filterElements(elements,
-     *     element -> ElementUtils.hasModifiers(element, Modifier.PRIVATE)
-     * );
-     *
-     * // Returns an empty list if input is null or predicates are null
-     * filtered = filterElements(null, (Predicate[]) null); // returns empty list
-     * }
- * - * @param elements The list of elements to be filtered, may be {@code null} - * @param elementPredicates An array of predicates used to filter the elements, may be {@code null} - * @param The type of the elements, which must be a subclass of {@link Element} - * @return A filtered list of elements that match all the provided predicates. Returns an empty list if no elements match, - * or if the input list or predicate array is invalid. - */ - @Nonnull - @Immutable - static List filterElements(List elements, Predicate... elementPredicates) { - if (isEmpty(elements) || elementPredicates == null) { - return emptyList(); - } - if (isNotEmpty(elementPredicates)) { - Predicate predicate = and(elementPredicates); - elements = (List) elements.stream().filter(predicate).collect(toList()); - } - return elements.isEmpty() ? emptyList() : unmodifiableList(elements); - } - - /** - * Checks whether the parameter types of the given {@link ExecutableElement} match the specified {@link Type types}. - * - *

- * This method compares the types of the parameters declared in the executable element with the provided expected types. - * It uses {@link TypeUtils#isSameType(Element, CharSequence)} to perform type comparison. - *

- * - *

Example Usage

- *
{@code
-     * ExecutableElement methodElement = ...; // an executable element with parameters
-     * boolean result = matchParameterTypes(methodElement, String.class, int.class); // returns true if parameter types match
-     *
-     * result = matchParameterTypes(null, String.class); // returns false
-     * result = matchParameterTypes(methodElement, (Type[]) null); // returns false
-     * }
- * - * @param executableElement the executable element whose parameters are to be checked, may be {@code null} - * @param parameterTypes the expected parameter types, may be {@code null} - * @return {@code true} if the parameter types match; {@code false} otherwise - */ - static boolean matchParameterTypes(ExecutableElement executableElement, Type... parameterTypes) { - return executableElement == null || parameterTypes == null ? false : - matchParameterTypeNames(executableElement.getParameters(), getTypeNames(parameterTypes)); - } - - /** - * Checks whether the parameter types of the given list of {@link VariableElement} parameters match the specified {@link Type types}. - * - *

- * This method compares each parameter's type with the corresponding expected type by their fully qualified type names. - * It uses {@link TypeUtils#isSameType(Element, CharSequence)} to perform the type comparison. - *

- * - *

Example Usage

- *
{@code
-     * List parameters = executableElement.getParameters();
-     * boolean result = matchParameterTypes(parameters, String.class, int.class); // returns true if types match
-     *
-     * result = matchParameterTypes(null, String.class); // returns false
-     * result = matchParameterTypes(parameters, (Type[]) null); // returns false
-     * }
- * - * @param parameters the list of variable elements representing the parameters, may be {@code null} - * @param parameterTypes the expected parameter types, may be {@code null} - * @return {@code true} if all parameter types match their corresponding expected types; otherwise, {@code false} - */ - static boolean matchParameterTypes(List parameters, Type... parameterTypes) { - return parameters == null || parameterTypes == null ? false : matchParameterTypeNames(parameters, getTypeNames(parameterTypes)); - } - - /** - * Checks whether the parameter types of the given list of {@link VariableElement} parameters match the specified type names. - * - *

- * This method compares each parameter's type with the corresponding expected type name using - * {@link TypeUtils#isSameType(Element, CharSequence)}. It ensures that both the number of parameters and their respective - * types match the provided array of type names. - *

- * - *

Example Usage

- *
{@code
-     * List parameters = executableElement.getParameters();
-     *
-     * // Check if the parameters match String and int
-     * boolean result = matchParameterTypeNames(parameters, "java.lang.String", "int"); // returns true if match
-     *
-     * // Returns false if either parameter list or type names are null
-     * result = matchParameterTypeNames(null, "java.lang.String"); // returns false
-     * result = matchParameterTypeNames(parameters, (CharSequence[]) null); // returns false
-     *
-     * // Returns false if the size of parameters and type names do not match
-     * result = matchParameterTypeNames(Arrays.asList(param1, param2), "java.lang.String"); // returns false
-     * }
- * - * @param parameters the list of variable elements representing the parameters, may be {@code null} - * @param parameterTypeNames the expected fully qualified type names of the parameters, may be {@code null} - * @return {@code true} if all parameter types match their corresponding type names; otherwise, {@code false} - */ - static boolean matchParameterTypeNames(List parameters, CharSequence... parameterTypeNames) { - if (parameters == null || parameterTypeNames == null) { - return false; - } - - int length = length(parameterTypeNames); - int size = parameters.size(); - - if (size != length) { - return false; - } - - for (int i = 0; i < size; i++) { - VariableElement parameter = parameters.get(i); - if (!isSameType(parameter, parameterTypeNames[i])) { - return false; - } - } - return true; - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ExecutableElementComparator.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ExecutableElementComparator.java deleted file mode 100644 index c7e561224..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/ExecutableElementComparator.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.util.CharSequenceComparator; - -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; -import java.util.Comparator; -import java.util.List; - -/** - * The Comparator class for {@link ExecutableElement}, the comparison rule : - *
    - *
  1. Comparing to two {@link ExecutableElement#getSimpleName() element names} {@link String#compareTo(String) lexicographically}. - * If equals, go to step 2
  2. - *
  3. Comparing to the count of two parameters. If equals, go to step 3
  4. - *
  5. Comparing to the type names of parameters {@link String#compareTo(String) lexicographically}
  6. - *
- * - *

Example Usage

- *
- * class Example {
- *     void methodA() {}
- *     void methodB() {}
- *     void methodB(String param1) {}
- *     void methodB(String param1, int param2) {}
- * }
- * 
- * - *

When comparing methods:

- *
    - *
  • {@code methodA} vs {@code methodB}: the names are compared lexicographically.
  • - *
  • {@code methodB()} vs {@code methodB(String)}: the number of parameters is compared.
  • - *
  • {@code methodB(String)} vs {@code methodB(String, int)}: the parameter type names are compared lexicographically.
  • - *
- * - * @author Mercy - * @since 1.0.0 - */ -public class ExecutableElementComparator implements Comparator { - - /** - * The singleton instance - */ - public static final ExecutableElementComparator INSTANCE = new ExecutableElementComparator(); - - private ExecutableElementComparator() { - } - - @Override - public int compare(ExecutableElement e1, ExecutableElement e2) { - - if (e1.equals(e2)) { - return 0; - } - - // Step 1 - int value = CharSequenceComparator.INSTANCE.compare(e1.getSimpleName(), e2.getSimpleName()); - - if (value == 0) { // Step 2 - - List ps1 = e1.getParameters(); - List ps2 = e2.getParameters(); - - value = ps1.size() - ps2.size(); - - if (value == 0) { // Step 3 - for (int i = 0; i < ps1.size(); i++) { - value = CharSequenceComparator.INSTANCE.compare(ps1.get(i).asType().toString(), ps2.get(i).asType().toString()); - if (value != 0) { - break; - } - } - } - } - return value; - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/FieldUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/FieldUtils.java deleted file mode 100644 index f1c195fd9..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/FieldUtils.java +++ /dev/null @@ -1,763 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.Immutable; -import io.microsphere.annotation.Nonnull; -import io.microsphere.annotation.Nullable; -import io.microsphere.util.Utils; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; -import java.util.List; -import java.util.function.Predicate; - -import static io.microsphere.annotation.processor.util.ElementUtils.filterElements; -import static io.microsphere.annotation.processor.util.ElementUtils.hasModifiers; -import static io.microsphere.annotation.processor.util.ElementUtils.matchesElementKind; -import static io.microsphere.annotation.processor.util.MemberUtils.getDeclaredMembers; -import static io.microsphere.annotation.processor.util.TypeUtils.isEnumType; -import static io.microsphere.collection.CollectionUtils.isEmpty; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; -import static io.microsphere.lang.function.Streams.filterFirst; -import static java.util.Collections.emptyList; -import static javax.lang.model.element.ElementKind.ENUM_CONSTANT; -import static javax.lang.model.element.ElementKind.FIELD; -import static javax.lang.model.element.Modifier.STATIC; -import static javax.lang.model.util.ElementFilter.fieldsIn; - -/** - * A utility interface for working with fields in the context of Java annotation processing. - *

- * This interface provides a collection of static methods to retrieve, filter, and inspect - * {@link VariableElement fields} within a class or interface. It supports operations such as: - *

- * - *
    - *
  • Retrieving fields by name
  • - *
  • Finding fields with specific modifiers (e.g., static, public)
  • - *
  • Filtering fields based on custom predicates
  • - *
  • Checking if a field is an enum constant or a non-static field
  • - *
  • Getting all declared or non-static fields from a type or element
  • - *
- * - *

Example Usage

- *
{@code
- * // Get a specific field by name
- * VariableElement field = FieldUtils.getDeclaredField(element, "myField");
- *
- * // Get all non-static fields
- * List nonStaticFields = FieldUtils.getNonStaticFields(type);
- *
- * // Find a field by name in the current class and its superclasses
- * VariableElement fieldInHierarchy = FieldUtils.findField(type, "myField");
- *
- * // Check if a field is non-static
- * boolean isNonStatic = FieldUtils.isNonStaticField(field);
- *
- * // Get all fields, including those from superclasses
- * List allFields = FieldUtils.getAllDeclaredFields(element);
- * }
- * - * @author
Mercy - * @since 1.0.0 - */ -public interface FieldUtils extends Utils { - - /** - * Retrieves the declared field with the specified name from the given element. - * - *

If the provided {@link Element} is null, this method returns null. It's typically used - * to retrieve a specific field from a class or interface element. - * - *

Example Usage

- *
{@code
-     * Element element = ...; // A class or interface element
-     * String fieldName = "myField";
-     * VariableElement field = getDeclaredField(element, fieldName);
-     * if (field != null) {
-     *     System.out.println("Found field: " + field.getSimpleName());
-     * } else {
-     *     System.out.println("Field not found.");
-     * }
-     * }
- * - * @param element the element to search for the field; if null, null is returned - * @param fieldName the name of the field to find - * @return the VariableElement representing the declared field, or null if not found - */ - @Nullable - static VariableElement getDeclaredField(Element element, String fieldName) { - return element == null ? null : getDeclaredField(element.asType(), fieldName); - } - - /** - * Retrieves the declared field with the specified name from the given type. - * - *

If the provided {@link TypeMirror} is null, this method returns null. - * It searches for a field with the exact specified name in the given type, - * and returns the first match found. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     * String fieldName = "myField";
-     * VariableElement field = getDeclaredField(type, fieldName);
-     * if (field != null) {
-     *     System.out.println("Found field: " + field.getSimpleName());
-     * } else {
-     *     System.out.println("Field not found.");
-     * }
-     * }
- * - * @param type the type to search for the field; if null, null is returned - * @param fieldName the name of the field to find - * @return the VariableElement representing the declared field, or null if not found - */ - @Nullable - static VariableElement getDeclaredField(TypeMirror type, String fieldName) { - return filterFirst(findDeclaredFields(type, field -> fieldName.equals(field.getSimpleName().toString()))); - } - - /** - * Retrieves all declared fields from the given element without any filtering. - * - *

If the provided {@link Element} is null, this method returns an empty list. - * It's typically used to get all fields declared in a class or interface, - * excluding fields from superclasses or interfaces. - * - *

Example Usage

- *
{@code
-     * Element element = ...; // A class or interface element
-     * List fields = getDeclaredFields(element);
-     * for (VariableElement field : fields) {
-     *     System.out.println("Field: " + field.getSimpleName());
-     * }
-     * }
- * - * @param element the element to retrieve declared fields from - * @return a list of VariableElement objects representing the declared fields, - * or an empty list if the element is null or no fields are found - */ - @Nonnull - @Immutable - static List getDeclaredFields(Element element) { - return findDeclaredFields(element, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves all declared fields from the given type without any filtering. - * - *

If the provided {@link TypeMirror} is null, this method returns an empty list. - * It's typically used to get all fields declared directly within a class or interface, - * excluding fields from superclasses or interfaces. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     * List fields = getDeclaredFields(type);
-     * for (VariableElement field : fields) {
-     *     System.out.println("Declared field: " + field.getSimpleName());
-     * }
-     * }
- * - * @param type the type to retrieve declared fields from - * @return a list of VariableElement objects representing the declared fields, - * or an empty list if the type is null or no fields are found - */ - @Nonnull - @Immutable - static List getDeclaredFields(TypeMirror type) { - return findDeclaredFields(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves all declared fields from the given element, including those from hierarchical types (e.g., superclasses), - * without any filtering. - * - *

If the provided {@link Element} is null, this method returns an empty list. It's typically used - * to get all fields declared directly within a class or interface, as well as those inherited from superclasses. - * - *

Example Usage

- *
{@code
-     * Element element = ...; // A class or interface element
-     * List fields = getAllDeclaredFields(element);
-     * for (VariableElement field : fields) {
-     *     System.out.println("Declared field (including hierarchical): " + field.getSimpleName());
-     * }
-     * }
- * - * @param element the element to retrieve all declared fields from - * @return a list of VariableElement objects representing all declared fields, - * or an empty list if the element is null or no fields are found - */ - @Nonnull - @Immutable - static List getAllDeclaredFields(Element element) { - return findAllDeclaredFields(element, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves all declared fields from the given type, including those from hierarchical types (e.g., superclasses), - * without any filtering. - * - *

If the provided {@link TypeMirror} is null, this method returns an empty list. It's typically used - * to get all fields declared directly within a class or interface, as well as those inherited from superclasses. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     * List fields = getAllDeclaredFields(type);
-     * for (VariableElement field : fields) {
-     *     System.out.println("Declared field (including hierarchical): " + field.getSimpleName());
-     * }
-     * }
- * - * @param type the type to retrieve all declared fields from - * @return a list of VariableElement objects representing all declared fields, - * or an empty list if the type is null or no fields are found - */ - @Nonnull - @Immutable - static List getAllDeclaredFields(TypeMirror type) { - return findAllDeclaredFields(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves the first matching field with the specified name from the given element. - * - *

If the provided {@link Element} is null, this method returns null. - * It searches for a field with the exact specified name in the given element - * and returns the first match found. - * - *

Example Usage

- *
{@code
-     * Element element = ...; // A valid class or interface element
-     * String fieldName = "myField";
-     * VariableElement field = findField(element, fieldName);
-     * if (field != null) {
-     *     System.out.println("Found field: " + field.getSimpleName());
-     * } else {
-     *     System.out.println("Field not found.");
-     * }
-     * }
- * - * @param element the element to search for the field; if null, null is returned - * @param fieldName the name of the field to find - * @return the VariableElement representing the first matching field, or null if not found - */ - @Nullable - static VariableElement findField(Element element, String fieldName) { - return element == null ? null : findField(element.asType(), fieldName); - } - - /** - * Retrieves the first matching field with the specified name from the given type. - * - *

This method searches for a field with the exact specified name in the given type, - * including all hierarchical types (e.g., superclasses), and returns the first match found. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     * String fieldName = "myField";
-     * VariableElement field = findField(type, fieldName);
-     * if (field != null) {
-     *     System.out.println("Found field: " + field.getSimpleName());
-     * } else {
-     *     System.out.println("Field not found.");
-     * }
-     * }
- * - * @param type the type to search for fields; if null, null is returned - * @param fieldName the name of the field to find - * @return the VariableElement representing the first matching field, or null if not found - */ - @Nullable - static VariableElement findField(TypeMirror type, String fieldName) { - return filterFirst(findAllDeclaredFields(type, field -> equalsFieldName(field, fieldName))); - } - - /** - * Retrieves the declared fields from the given element after applying the provided filters. - * - *

If the provided {@link Element} is null, this method returns an empty list. It searches - * for fields directly declared in the given element (excluding fields from superclasses or interfaces) - * and applies the specified filters to narrow down the results. - * - *

Example Usage

- *
{@code
-     * Element element = ...; // A class or interface element
-     * List allFields = findDeclaredFields(element); // Get all declared fields
-     *
-     * // Get only non-static fields
-     * List nonStaticFields = findDeclaredFields(element, FieldUtils::isNonStaticField);
-     *
-     * // Get fields matching a specific name
-     * String fieldName = "myField";
-     * List matchingFields = findDeclaredFields(element, field -> fieldName.equals(field.getSimpleName().toString()));
-     *
-     * // Get fields with multiple filters (e.g., non-static and public)
-     * List publicNonStaticFields = findDeclaredFields(element,
-     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
-     *     FieldUtils::isNonStaticField
-     * );
-     * }
- * - * @param element the element to retrieve declared fields from; if null, an empty list is returned - * @param fieldFilters the predicates used to filter the fields; optional - * @return a list of VariableElement objects representing the filtered declared fields - */ - @Nonnull - @Immutable - static List findDeclaredFields(Element element, Predicate... fieldFilters) { - return element == null ? emptyList() : findDeclaredFields(element.asType(), fieldFilters); - } - - /** - * Retrieves the declared fields from the given type after applying the provided filters. - * - *

This method searches for fields directly declared in the given type, - * excluding fields from superclasses or interfaces. It allows filtering - * the fields using the provided predicates to narrow down the results. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     *
-     * // Get all declared fields
-     * List allFields = findDeclaredFields(type);
-     *
-     * // Get only non-static fields
-     * List nonStaticFields = findDeclaredFields(type, FieldUtils::isNonStaticField);
-     *
-     * // Get fields matching a specific name
-     * String fieldName = "myField";
-     * List matchingFields = findDeclaredFields(type, field -> "myField".equals(field.getSimpleName().toString()));
-     *
-     * // Get fields with multiple filters (e.g., non-static and public)
-     * List publicNonStaticFields = findDeclaredFields(type,
-     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
-     *     FieldUtils::isNonStaticField
-     * );
-     * }
- * - * @param type the type to retrieve declared fields from; if null, an empty list is returned - * @param fieldFilters the predicates used to filter the fields - * @return a list of VariableElement objects representing the filtered declared fields - */ - @Nonnull - @Immutable - static List findDeclaredFields(TypeMirror type, Predicate... fieldFilters) { - return filterDeclaredFields(type, false, fieldFilters); - } - - /** - * Retrieves all declared fields from the given element, including those from hierarchical types (e.g., superclasses), - * after applying the provided filters. - * - *

This method processes the given element and searches for all fields declared directly within it - * as well as those inherited from superclasses. The fields can be filtered using one or more predicates - * to narrow down the results. - * - *

Example Usage

- *
{@code
-     * Element element = ...; // A valid class or interface element
-     *
-     * // Get all declared fields (including from superclasses)
-     * List allFields = findAllDeclaredFields(element);
-     *
-     * // Get only non-static fields
-     * List nonStaticFields = findAllDeclaredFields(element, FieldUtils::isNonStaticField);
-     *
-     * // Get fields matching a specific name
-     * String fieldName = "myField";
-     * List matchingFields = findAllDeclaredFields(element, field -> "myField".equals(field.getSimpleName().toString()));
-     *
-     * // Get fields with multiple filters (e.g., non-static and public)
-     * List publicNonStaticFields = findAllDeclaredFields(element,
-     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
-     *     FieldUtils::isNonStaticField
-     * );
-     * }
- * - * @param element the element to retrieve all declared fields from; if null, an empty list is returned - * @param fieldFilters the predicates used to filter the fields; optional - * @return a list of VariableElement objects representing the filtered declared fields - */ - @Nonnull - @Immutable - static List findAllDeclaredFields(Element element, Predicate... fieldFilters) { - return element == null ? emptyList() : findAllDeclaredFields(element.asType(), fieldFilters); - } - - /** - * Retrieves all declared fields from the given type, including those from hierarchical types (e.g., superclasses), - * after applying the provided filters. - * - *

This method searches for fields directly declared in the given type as well as those inherited - * from superclasses. It allows filtering the fields using the provided predicates to narrow down the results. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     *
-     * // Get all declared fields (including hierarchical)
-     * List allFields = findAllDeclaredFields(type);
-     *
-     * // Get only non-static fields
-     * List nonStaticFields = findAllDeclaredFields(type, FieldUtils::isNonStaticField);
-     *
-     * // Get fields matching a specific name
-     * String fieldName = "myField";
-     * List matchingFields = findAllDeclaredFields(type, field -> "myField".equals(field.getSimpleName().toString()));
-     *
-     * // Get fields with multiple filters (e.g., non-static and public)
-     * List publicNonStaticFields = findAllDeclaredFields(type,
-     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
-     *     FieldUtils::isNonStaticField
-     * );
-     * }
- * - * @param type the type to retrieve all declared fields from; if null, an empty list is returned - * @param fieldFilters the predicates used to filter the fields - * @return a list of VariableElement objects representing the filtered declared fields - */ - @Nonnull - @Immutable - static List findAllDeclaredFields(TypeMirror type, Predicate... fieldFilters) { - return filterDeclaredFields(type, true, fieldFilters); - } - - /** - * Filters and retrieves the declared fields from the given type based on the provided criteria. - * - *

This method is used to retrieve fields declared in the given type, optionally including fields - * from its superclasses or interfaces. The fields can be further filtered using one or more predicates - * to narrow down the results. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     *
-     * // Get all declared fields (excluding hierarchical)
-     * List declaredFields = filterDeclaredFields(type, false);
-     *
-     * // Get all declared fields including hierarchical ones
-     * List allFields = filterDeclaredFields(type, true);
-     *
-     * // Get only non-static fields
-     * List nonStaticFields = filterDeclaredFields(type, false, FieldUtils::isNonStaticField);
-     *
-     * // Get fields matching a specific name
-     * String fieldName = "myField";
-     * List matchingFields = filterDeclaredFields(type, false,
-     *     field -> "myField".equals(field.getSimpleName().toString()));
-     *
-     * // Get fields with multiple filters (e.g., non-static and public)
-     * List publicNonStaticFields = filterDeclaredFields(type, true,
-     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
-     *     FieldUtils::isNonStaticField
-     * );
-     * }
- * - * @param type the type to retrieve declared fields from; if null, an empty list is returned - * @param includeHierarchicalTypes whether to include fields from hierarchical types (e.g., superclasses) - * @param fieldFilters the predicates used to filter the fields; optional - * @return a list of VariableElement objects representing the filtered declared fields - */ - @Nonnull - @Immutable - static List filterDeclaredFields(TypeMirror type, boolean includeHierarchicalTypes, Predicate... fieldFilters) { - if (type == null) { - return emptyList(); - } - - List declaredMembers = getDeclaredMembers(type, includeHierarchicalTypes); - if (isEmpty(declaredMembers)) { - return emptyList(); - } - - List fields = fieldsIn(declaredMembers); - - return filterElements(fields, fieldFilters); - } - - /** - * Determines whether the given field is an enum member field. - * - *

An enum member field is typically a public static final field that represents - * a constant within an enum declaration. This method checks if the field's enclosing - * element is an enum and if the field's kind matches {@link ElementKind#ENUM_CONSTANT}. - * - *

Example Usage

- *
{@code
-     * VariableElement field = ...; // A valid field element
-     * boolean isEnumMember = FieldUtils.isEnumMemberField(field);
-     * if (isEnumMember) {
-     *     System.out.println("The field is an enum member field.");
-     * } else {
-     *     System.out.println("The field is not an enum member field.");
-     * }
-     * }
- * - * @param field the field to check; may be null - * @return true if the field is an enum member field, false otherwise - */ - static boolean isEnumMemberField(VariableElement field) { - if (field == null || !isEnumType(field.getEnclosingElement())) { - return false; - } - return ENUM_CONSTANT.equals(field.getKind()); - } - - /** - * Checks if the given field is a non-static field. - * - *

This method verifies whether the provided {@link VariableElement} represents a field - * that is not declared with the {@code static} modifier. It first checks if the element is - * a valid field (including enum constants) using the {@link #isField(VariableElement)} method, - * and then ensures that the field does not have the static modifier. - * - *

Example Usage

- *
{@code
-     * VariableElement field = ...; // A valid field element
-     * boolean isNonStatic = FieldUtils.isNonStaticField(field);
-     * if (isNonStatic) {
-     *     System.out.println("The field is non-static.");
-     * } else {
-     *     System.out.println("The field is static or not a valid field.");
-     * }
-     * }
- * - * @param field the VariableElement to check; may be null - * @return true if the field is a valid field (as per {@link #isField(VariableElement)}) - * and does not have the 'static' modifier, false otherwise - */ - static boolean isNonStaticField(VariableElement field) { - return isField(field) && !hasModifiers(field, STATIC); - } - - /** - * Checks if the given element is a field or an enum constant. - * - *

This method determines whether the provided {@link VariableElement} represents a valid field - * or an enum constant. It returns {@code true} if the element's kind is either - * {@link ElementKind#FIELD} or {@link ElementKind#ENUM_CONSTANT}. - * - *

Example Usage

- *
{@code
-     * VariableElement field = ...; // A valid field element
-     * boolean isValidField = FieldUtils.isField(field);
-     * if (isValidField) {
-     *     System.out.println("The element is a valid field or enum constant.");
-     * } else {
-     *     System.out.println("The element is not a field or enum constant.");
-     * }
-     * }
- * - * @param field the VariableElement to check; may be null - * @return true if the element is a field ({@link javax.lang.model.element.ElementKind#FIELD}) - * or an enum constant ({@link javax.lang.model.element.ElementKind#ENUM_CONSTANT}), false otherwise - */ - static boolean isField(VariableElement field) { - return matchesElementKind(field, FIELD) || isEnumMemberField(field); - } - - /** - * Checks if the given element is a field or an enum constant, and also has all the specified modifiers. - * - *

This method extends the {@link #isField(VariableElement)} method by additionally verifying that - * the field has all of the specified modifiers. It returns {@code true} only if the element is a valid field - * (including enum constants) and contains all the provided modifiers. - * - *

Example Usage

- *
{@code
-     * VariableElement field = ...; // A valid field element
-     *
-     * // Check if it's a public static field
-     * boolean isPublicStaticField = FieldUtils.isField(field, Modifier.PUBLIC, Modifier.STATIC);
-     * if (isPublicStaticField) {
-     *     System.out.println("The field is a public static field.");
-     * } else {
-     *     System.out.println("The field is not a public static field.");
-     * }
-     *
-     * // Check if it's a private field
-     * boolean isPrivateField = FieldUtils.isField(field, Modifier.PRIVATE);
-     * if (isPrivateField) {
-     *     System.out.println("The field is a private field.");
-     * } else {
-     *     System.out.println("The field is not a private field.");
-     * }
-     * }
- * - * @param field the VariableElement to check; may be null - * @param modifiers the modifiers to match (e.g., Modifier.PUBLIC, Modifier.STATIC) - * @return true if the element is a field ({@link javax.lang.model.element.ElementKind#FIELD}) - * or an enum constant ({@link javax.lang.model.element.ElementKind#ENUM_CONSTANT}), - * and it has all of the specified modifiers, false otherwise - */ - static boolean isField(VariableElement field, Modifier... modifiers) { - return isField(field) && hasModifiers(field, modifiers); - } - - /** - * Retrieves all declared non-static fields from the given type. - * - *

This method returns a list of fields that are declared directly within the given type - * and are not marked as static. It does not include fields from superclasses or interfaces. - * If the provided {@link TypeMirror} is null, this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     * List nonStaticFields = FieldUtils.getNonStaticFields(type);
-     * for (VariableElement field : nonStaticFields) {
-     *     System.out.println("Non-static field: " + field.getSimpleName());
-     * }
-     * }
- * - * @param type the type to search for non-static fields; if null, an empty list is returned - * @return a list of VariableElement objects representing the non-static fields, - * or an empty list if the type is null or no non-static fields are found - */ - @Nonnull - @Immutable - static List getNonStaticFields(TypeMirror type) { - return findDeclaredFields(type, FieldUtils::isNonStaticField); - } - - /** - * Retrieves all declared non-static fields from the given element. - * - *

This method processes the provided element and retrieves all fields directly declared - * within it that are not marked as static. It excludes fields from superclasses or interfaces. - * If the provided element is null, this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * Element element = ...; // A valid class or interface element
-     * List nonStaticFields = FieldUtils.getNonStaticFields(element);
-     * for (VariableElement field : nonStaticFields) {
-     *     System.out.println("Non-static field: " + field.getSimpleName());
-     * }
-     *
-     * // Handling null case
-     * List safeList = FieldUtils.getNonStaticFields(null);
-     * System.out.println(safeList.isEmpty()); // true
-     * }
- * - * @param element the element to search for non-static fields; if null, an empty list is returned - * @return a list of VariableElement objects representing the non-static fields, - * or an empty list if the element is null or no non-static fields are found - */ - @Nonnull - @Immutable - static List getNonStaticFields(Element element) { - return element == null ? emptyList() : getNonStaticFields(element.asType()); - } - - /** - * Retrieves all non-static fields from the given type, including those from hierarchical types (e.g., superclasses). - * - *

This method searches for all fields declared directly within the given type, as well as those inherited - * from its superclasses or interfaces, and filters out only the non-static fields. If the provided {@link TypeMirror} - * is null, this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = element.asType(); // A valid type from an element
-     * List nonStaticFields = FieldUtils.getAllNonStaticFields(type);
-     * for (VariableElement field : nonStaticFields) {
-     *     System.out.println("Non-static field (including hierarchical): " + field.getSimpleName());
-     * }
-     *
-     * // Handling null case
-     * List safeList = FieldUtils.getAllNonStaticFields(null);
-     * System.out.println(safeList.isEmpty()); // true
-     * }
- * - * @param type the type to retrieve all non-static fields from; if null, an empty list is returned - * @return a list of VariableElement objects representing all non-static fields, - * or an empty list if the type is null or no non-static fields are found - */ - @Nonnull - @Immutable - static List getAllNonStaticFields(TypeMirror type) { - return findAllDeclaredFields(type, FieldUtils::isNonStaticField); - } - - /** - * Retrieves all non-static fields from the given element, including those from superclasses and interfaces. - * - *

This method processes the provided element and retrieves all fields declared directly within it, - * as well as those inherited from superclasses or interfaces. It filters out only the non-static fields. - * If the provided element is null, this method returns an empty list. - * - *

Example Usage

- *
{@code
-     * Element element = ...; // A valid class or interface element
-     * List nonStaticFields = FieldUtils.getAllNonStaticFields(element);
-     * for (VariableElement field : nonStaticFields) {
-     *     System.out.println("Non-static field: " + field.getSimpleName());
-     * }
-     *
-     * // Handling null case
-     * List safeList = FieldUtils.getAllNonStaticFields(null);
-     * System.out.println(safeList.isEmpty()); // true
-     * }
- * - * @param element the element to retrieve all non-static fields from; if null, an empty list is returned - * @return a list of VariableElement objects representing all non-static fields, - * or an empty list if the element is null or no non-static fields are found - */ - @Nonnull - @Immutable - static List getAllNonStaticFields(Element element) { - return element == null ? emptyList() : getAllNonStaticFields(element.asType()); - } - - /** - * Checks if the simple name of the given field matches the specified field name. - * - *

This method ensures both the {@link VariableElement} and the field name are non-null - * before comparing their string representations for equality. - * - *

Example Usage

- *
{@code
-     * VariableElement field = ...; // A valid field element
-     * CharSequence fieldName = "myField";
-     * boolean isMatch = equalsFieldName(field, fieldName);
-     * if (isMatch) {
-     *     System.out.println("Field name matches: " + fieldName);
-     * } else {
-     *     System.out.println("Field name does not match.");
-     * }
-     * }
- * - * @param field the VariableElement representing the field; may be null - * @param fieldName the CharSequence representing the expected field name; may be null - * @return true if both the field and fieldName are non-null and their string representations match, false otherwise - */ - static boolean equalsFieldName(VariableElement field, CharSequence fieldName) { - return field != null && fieldName != null && field.getSimpleName().toString().equals(fieldName.toString()); - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/LoggerUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/LoggerUtils.java deleted file mode 100644 index 17b69dbeb..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/LoggerUtils.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - - -import io.microsphere.logging.Logger; -import io.microsphere.util.Utils; - -import static io.microsphere.logging.LoggerFactory.getLogger; - -/** - * Logger Utils - * - * @author Mercy - * @since 1.0.0 - */ -public interface LoggerUtils extends Utils { - - Logger LOGGER = getLogger("microsphere-annotation-processor"); - - static void trace(String format, Object... args) { - LOGGER.trace(format, args); - } - - static void debug(String format, Object... args) { - LOGGER.debug(format, args); - } - - static void info(String format, Object... args) { - LOGGER.info(format, args); - } - - static void warn(String format, Object... args) { - LOGGER.warn(format, args); - } - - static void error(String format, Object... args) { - LOGGER.error(format, args); - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MemberUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MemberUtils.java deleted file mode 100644 index 46c1d9270..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MemberUtils.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.Immutable; -import io.microsphere.annotation.Nonnull; -import io.microsphere.util.Utils; - -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeMirror; -import java.util.Collection; -import java.util.List; -import java.util.function.Predicate; - -import static io.microsphere.annotation.processor.util.ElementUtils.filterElements; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllDeclaredTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.ofTypeElement; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; -import static java.util.Collections.emptyList; -import static java.util.stream.Collectors.toList; - -/** - * A utility interface for handling members (such as fields, methods, constructors) from types - * in the {@link javax.lang.model} package. - * - *

Overview

- *

- * This interface provides a set of static methods to retrieve and filter members of a type, - * including direct members and those inherited from superclasses and interfaces. - * It is designed to simplify the process of working with type elements and their enclosed elements - * during annotation processing. - *

- * - *

Key Features

- *
    - *
  • {@link #getDeclaredMembers(TypeMirror) getDeclaredMembers(TypeMirror)} - Retrieves directly declared members of a type.
  • - *
  • {@link #getAllDeclaredMembers(TypeMirror) getAllDeclaredMembers(TypeMirror)} - Retrieves all declared members, including those from superclasses and interfaces.
  • - *
  • {@link #findDeclaredMembers(TypeMirror, Predicate...) findDeclaredMembers(TypeMirror, Predicate...)} - Retrieves and filters declared members based on provided predicates.
  • - *
  • {@link #findAllDeclaredMembers(TypeMirror, Predicate...) findAllDeclaredMembers(TypeMirror, Predicate...)} - Retrieves all declared members and applies filtering via predicates.
  • - *
- * - *

Example Usage

- *
{@code
- * // Retrieve all declared methods from a TypeMirror
- * List methods = findDeclaredMembers(typeMirror,
- *     element -> element.getKind() == ElementKind.METHOD
- * );
- * }
- * - *
{@code
- * // Retrieve all fields that start with "m_"
- * List filteredFields = findDeclaredMembers(typeElement,
- *     element -> element.getKind() == ElementKind.FIELD,
- *     element -> element.getSimpleName().toString().startsWith("m_")
- * );
- * }
- * - * @author
Mercy - * @since 1.0.0 - */ -public interface MemberUtils extends Utils { - - /** - * Returns the directly declared members of the provided {@link TypeMirror}. - * If the given type is {@code null}, an empty list will be returned. - * - *

Example Usage

- *
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
-     * List members = getDeclaredMembers(typeMirror);
-     * if (!members.isEmpty()) {
-     *     for (Element member : members) {
-     *         System.out.println("Declared member: " + member);
-     *     }
-     * }
-     * 
- * - * @param type the type mirror to retrieve declared members from - * @return a list of directly declared members, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List getDeclaredMembers(TypeMirror type) { - return type == null ? emptyList() : getDeclaredMembers(ofTypeElement(type)); - } - - /** - * Returns the directly declared members of the provided {@link TypeElement}. - * If the given type is {@code null}, an empty list will be returned. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
-     * List members = getDeclaredMembers(typeElement);
-     * if (!members.isEmpty()) {
-     *     for (Element member : members) {
-     *         System.out.println("Declared member: " + member);
-     *     }
-     * }
-     * }
- * - * @param type the type element to retrieve declared members from - * @return a list of directly declared members, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List getDeclaredMembers(TypeElement type) { - return type == null ? emptyList() : findDeclaredMembers(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Returns all declared members (including those from superclasses and interfaces) of the provided {@link TypeMirror}. - * If the given type is {@code null}, an empty list will be returned. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
-     * List allMembers = getAllDeclaredMembers(typeMirror);
-     * if (!allMembers.isEmpty()) {
-     *     for (Element member : allMembers) {
-     *         System.out.println("All declared member: " + member);
-     *     }
-     * }
-     * }
- * - * @param type the type mirror to retrieve all declared members from - * @return a list of all declared members, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List getAllDeclaredMembers(TypeMirror type) { - return type == null ? emptyList() : findAllDeclaredMembers(ofTypeElement(type), EMPTY_PREDICATE_ARRAY); - } - - /** - * Returns all declared members (including those from superclasses and interfaces) of the provided {@link TypeElement}. - * If the given type is {@code null}, an empty list will be returned. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
-     * List allMembers = getAllDeclaredMembers(typeElement);
-     * if (!allMembers.isEmpty()) {
-     *     for (Element member : allMembers) {
-     *         System.out.println("All declared member: " + member);
-     *     }
-     * }
-     * }
- * - * @param type the type element to retrieve all declared members from - * @return a list of all declared members, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List getAllDeclaredMembers(TypeElement type) { - return type == null ? emptyList() : findAllDeclaredMembers(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Returns the declared members of the provided {@link TypeMirror}, optionally including - * members from superclasses and interfaces. - * - *

If the given type is {@code null}, an empty list will be returned.

- * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
-     * boolean includeSuperMembers = true; // Include members from superclasses/interfaces
-     * List members = getDeclaredMembers(typeMirror, includeSuperMembers);
-     * for (Element member : members) {
-     *     System.out.println("Member: " + member);
-     * }
-     * }
- * - * @param type the type mirror to retrieve declared members from - * @param includeHierarchicalTypes whether to include members from superclasses and interfaces - * @return a list of declared members, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List getDeclaredMembers(TypeMirror type, boolean includeHierarchicalTypes) { - return includeHierarchicalTypes ? getAllDeclaredMembers(type) : getDeclaredMembers(type); - } - - /** - * Returns the declared members of the provided {@link TypeElement}, optionally including - * members from superclasses and interfaces. - * - *

If the given type is {@code null}, an empty list will be returned.

- * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
-     * boolean includeSuperMembers = true; // Include members from superclasses/interfaces
-     * List members = getDeclaredMembers(typeElement, includeSuperMembers);
-     * for (Element member : members) {
-     *     System.out.println("Member: " + member);
-     * }
-     * }
- * - * @param type the type element to retrieve declared members from - * @param includeHierarchicalTypes whether to include members from superclasses and interfaces - * @return a list of declared members, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List getDeclaredMembers(TypeElement type, boolean includeHierarchicalTypes) { - return includeHierarchicalTypes ? getAllDeclaredMembers(type) : getDeclaredMembers(type); - } - - /** - * Returns the declared members of the provided {@link TypeMirror}, optionally filtered by one or more predicates. - * - *

If the given type is {@code null}, an empty list will be returned.

- * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
-     * List methods = findDeclaredMembers(typeMirror,
-     *     element -> element.getKind() == ElementKind.METHOD,
-     *     element -> element.getSimpleName().toString().startsWith("get")
-     * );
-     * for (Element method : methods) {
-     *     System.out.println("Matching method: " + method);
-     * }
-     * }
- * - * @param the type of elements to filter - * @param type the type mirror to retrieve declared members from - * @param memberFilters the predicates used to filter members - * @return a list of declared members matching the filters, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List findDeclaredMembers(TypeMirror type, Predicate... memberFilters) { - return type == null ? emptyList() : findDeclaredMembers(ofTypeElement(type), memberFilters); - } - - /** - * Returns the directly declared members of the provided {@link TypeElement}, optionally filtered by one or more predicates. - * - *

If the given type is {@code null}, an empty list will be returned.

- * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
-     * List fields = findDeclaredMembers(typeElement,
-     *     element -> element.getKind() == ElementKind.FIELD,
-     *     element -> element.getSimpleName().toString().startsWith("m_")
-     * );
-     * for (Element field : fields) {
-     *     System.out.println("Matching field: " + field);
-     * }
-     * }
- * - * @param the type of elements to filter - * @param type the type element to retrieve declared members from - * @param memberFilters the predicates used to filter members - * @return a list of declared members matching the filters, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List findDeclaredMembers(TypeElement type, Predicate... memberFilters) { - if (type == null) { - return emptyList(); - } - return filterElements((List) type.getEnclosedElements(), memberFilters); - } - - /** - * Returns all declared members (including those from superclasses and interfaces) of the provided {@link TypeMirror}, - * optionally filtered by one or more predicates. - * - *

If the given type is {@code null}, an empty list will be returned.

- * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
-     * List methods = findAllDeclaredMembers(typeMirror,
-     *     element -> element.getKind() == ElementKind.METHOD,
-     *     element -> element.getSimpleName().toString().startsWith("get")
-     * );
-     * for (Element method : methods) {
-     *     System.out.println("Matching method: " + method);
-     * }
-     * }
- * - * @param the type of elements to filter - * @param type the type mirror to retrieve all declared members from - * @param memberFilters the predicates used to filter members - * @return a list of all declared members matching the filters, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List findAllDeclaredMembers(TypeMirror type, Predicate... memberFilters) { - return type == null ? emptyList() : findAllDeclaredMembers(ofTypeElement(type), memberFilters); - } - - /** - * Retrieves all declared members (fields, methods, constructors, etc.) from the given {@link TypeElement}, - * including those inherited from superclasses and implemented interfaces. - * - *

This method collects all declared members by traversing the type hierarchy and applying - * the provided filters to narrow down the results. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
-     * List fields = findAllDeclaredMembers(typeElement,
-     *     element -> element.getKind() == ElementKind.FIELD
-     * );
-     * for (Element field : fields) {
-     *     System.out.println("Field: " + field);
-     * }
-     * }
- * - *
{@code
-     * List methods = findAllDeclaredMembers(typeElement,
-     *     element -> element.getKind() == ElementKind.METHOD,
-     *     element -> element.getSimpleName().toString().startsWith("get")
-     * );
-     * for (Element method : methods) {
-     *     System.out.println("Getter method: " + method);
-     * }
-     * }
- * - * @param the type of elements to filter - * @param type the type element to retrieve all declared members from - * @param memberFilters the predicates used to filter members - * @return a list of all declared members matching the filters, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List findAllDeclaredMembers(TypeElement type, Predicate... memberFilters) { - if (type == null) { - return emptyList(); - } - List declaredMembers = (List) getAllDeclaredTypes(type) - .stream() - .map(MemberUtils::getDeclaredMembers) - .flatMap(Collection::stream) - .collect(toList()); - return filterElements(declaredMembers, memberFilters); - } - - /** - * Returns the declared members of the provided {@link TypeMirror}, optionally including - * members from superclasses and interfaces, and filtered by one or more predicates. - * - *

If the given type is {@code null}, an empty list will be returned.

- * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
-     * boolean includeSuperMembers = true; // Include members from superclasses/interfaces
-     * List methods = findDeclaredMembers(typeMirror, includeSuperMembers,
-     *     element -> element.getKind() == ElementKind.METHOD,
-     *     element -> element.getSimpleName().toString().startsWith("get")
-     * );
-     * for (Element method : methods) {
-     *     System.out.println("Matching method: " + method);
-     * }
-     * }
- * - * @param the type of elements to filter - * @param type the type mirror to retrieve declared members from - * @param includeHierarchicalTypes whether to include members from superclasses and interfaces - * @param memberFilters the predicates used to filter members - * @return a list of declared members matching the filters, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List findDeclaredMembers(TypeMirror type, boolean includeHierarchicalTypes, Predicate... memberFilters) { - return includeHierarchicalTypes ? findAllDeclaredMembers(type, memberFilters) : findDeclaredMembers(type, memberFilters); - } - - /** - * Returns the declared members of the provided {@link TypeElement}, optionally including - * members from superclasses and interfaces, and filtered by one or more predicates. - * - *

If the given type is {@code null}, an empty list will be returned.

- * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
-     * boolean includeSuperMembers = true; // Include members from superclasses/interfaces
-     * List fields = findDeclaredMembers(typeElement, includeSuperMembers,
-     *     element -> element.getKind() == ElementKind.FIELD,
-     *     element -> element.getSimpleName().toString().startsWith("m_")
-     * );
-     * for (Element field : fields) {
-     *     System.out.println("Matching field: " + field);
-     * }
-     * }
- * - * @param the type of elements to filter - * @param type the type element to retrieve declared members from - * @param all whether to include members from superclasses and interfaces - * @param memberFilters the predicates used to filter members - * @return a list of declared members matching the filters, or an empty list if the type is {@code null} - */ - @Nonnull - @Immutable - static List findDeclaredMembers(TypeElement type, boolean all, Predicate... memberFilters) { - return all ? findAllDeclaredMembers(type, memberFilters) : findDeclaredMembers(type, memberFilters); - } - -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MessagerUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MessagerUtils.java deleted file mode 100644 index 7668c478d..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MessagerUtils.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional inpatternion 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. - */ - -package io.microsphere.annotation.processor.util; - -import io.microsphere.util.Utils; - -import javax.annotation.processing.Messager; -import javax.annotation.processing.ProcessingEnvironment; -import javax.tools.Diagnostic.Kind; - -import static io.microsphere.annotation.processor.util.LoggerUtils.debug; -import static io.microsphere.annotation.processor.util.LoggerUtils.error; -import static io.microsphere.annotation.processor.util.LoggerUtils.info; -import static io.microsphere.annotation.processor.util.LoggerUtils.warn; -import static io.microsphere.text.FormatUtils.format; -import static javax.tools.Diagnostic.Kind.ERROR; -import static javax.tools.Diagnostic.Kind.MANDATORY_WARNING; -import static javax.tools.Diagnostic.Kind.NOTE; -import static javax.tools.Diagnostic.Kind.WARNING; - -/** - * {@link Messager} utilities class - * - * @author Mercy - * @see Messager - * @since 1.0.0 - */ -public interface MessagerUtils extends Utils { - - /** - * Prints a note message using the {@link ProcessingEnvironment}'s {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and delegates to the underlying {@link Messager} obtained from the processing environment. - *

- * - *

Example Usage

- *
-     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
-     * printNote(processingEnv, "Found {0} elements matching criteria", count);
-     * 
- * - * @param processingEnv the processing environment to obtain the messager from - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printNote(ProcessingEnvironment processingEnv, String pattern, Object... args) { - printNote(processingEnv.getMessager(), pattern, args); - } - - /** - * Prints a note message using the provided {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and forwards it to the underlying {@link Messager}. - *

- * - *

Example Usage

- *
-     * Messager messager = processingEnv.getMessager();
-     * printNote(messager, "Found {0} elements matching criteria", count);
-     * 
- * - * @param messager the messager to use for printing the message - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - /** - * Prints a note message using the provided {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and forwards it to the underlying {@link Messager}. - *

- * - *

Example Usage

- *
-     * Messager messager = processingEnv.getMessager();
-     * printNote(messager, "Found {0} elements matching criteria", count);
-     * 
- * - * @param messager the messager to use for printing the message - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printNote(Messager messager, String pattern, Object... args) { - printMessage(messager, NOTE, pattern, args); - } - - /** - * Prints a warning message using the {@link ProcessingEnvironment}'s {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and delegates to the underlying {@link Messager} obtained from the processing environment. - * The message will be logged at the warning level through both the {@link Messager} - * and internal logging utilities. - *

- * - *

Example Usage

- *
-     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
-     * printWarning(processingEnv, "Found {0} deprecated elements", count);
-     * 
- * - * @param processingEnv the processing environment to obtain the messager from - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printWarning(ProcessingEnvironment processingEnv, String pattern, Object... args) { - printWarning(processingEnv.getMessager(), pattern, args); - } - - /** - * Prints a warning message using the provided {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and forwards it to the underlying {@link Messager} as a warning level message. - * The message will also be logged through internal logging utilities at the warning level. - *

- * - *

Example Usage

- *
-     * Messager messager = processingEnv.getMessager();
-     * printWarning(messager, "Found {0} deprecated elements", count);
-     * 
- * - * @param messager the messager to use for printing the message - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printWarning(Messager messager, String pattern, Object... args) { - printMessage(messager, WARNING, pattern, args); - } - - /** - * Prints a mandatory warning message using the {@link ProcessingEnvironment}'s {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and delegates to the underlying {@link Messager} obtained from the processing environment. - * The message will be logged at the warning level through both the {@link Messager} - * and internal logging utilities. - *

- * - *

Example Usage

- *
-     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
-     * printMandatoryWarning(processingEnv, "Found {0} obsolete elements", count);
-     * 
- * - * @param processingEnv the processing environment to obtain the messager from - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printMandatoryWarning(ProcessingEnvironment processingEnv, String pattern, Object... args) { - printMandatoryWarning(processingEnv.getMessager(), pattern, args); - } - - /** - * Prints a mandatory warning message using the provided {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and forwards it to the underlying {@link Messager} as a mandatory warning level message. - * The message will also be logged through internal logging utilities at the warning level. - *

- * - *

Example Usage

- *
-     * Messager messager = processingEnv.getMessager();
-     * printMandatoryWarning(messager, "Found {0} obsolete elements", count);
-     * 
- * - * @param messager the messager to use for printing the message - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printMandatoryWarning(Messager messager, String pattern, Object... args) { - printMessage(messager, MANDATORY_WARNING, pattern, args); - } - - /** - * Prints an error message using the {@link ProcessingEnvironment}'s {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and delegates to the underlying {@link Messager} obtained from the processing environment. - * The message will be logged at the error level through both the {@link Messager} - * and internal logging utilities. - *

- * - *

Example Usage

- *
-     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
-     * printError(processingEnv, "Failed to process {0} elements", count);
-     * 
- * - * @param processingEnv the processing environment to obtain the messager from - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printError(ProcessingEnvironment processingEnv, String pattern, Object... args) { - printError(processingEnv.getMessager(), pattern, args); - } - - /** - * Prints an error message using the provided {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * and forwards it to the underlying {@link Messager} as an error level message. - * The message will also be logged through internal logging utilities at the error level. - *

- * - *

Example Usage

- *
-     * Messager messager = processingEnv.getMessager();
-     * printError(messager, "Failed to process {0} elements", count);
-     * 
- * - * @param messager the messager to use for printing the message - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printError(Messager messager, String pattern, Object... args) { - printMessage(messager, ERROR, pattern, args); - } - - /** - * Prints a message of the specified kind using the {@link ProcessingEnvironment}'s {@link Messager}. - * - *

- * This method retrieves the {@link Messager} from the provided {@link ProcessingEnvironment} - * and delegates to the {@link #printMessage(Messager, Kind, String, Object...)} method - * to handle message formatting and output. - *

- * - *

Example Usage

- *
-     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
-     * printMessage(processingEnv, Kind.WARNING, "Found {0} deprecated elements", count);
-     * 
- * - * @param processingEnv the processing environment to obtain the messager from - * @param kind the kind of message to print (e.g., error, warning, note) - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printMessage(ProcessingEnvironment processingEnv, Kind kind, String pattern, Object... args) { - printMessage(processingEnv.getMessager(), kind, pattern, args); - } - - /** - * Prints a message of the specified kind using the provided {@link Messager}. - * - *

- * This method formats the message using the provided pattern and arguments, - * sends it to the annotation processor's messager, and also logs it using - * internal logging utilities at the appropriate level. - *

- * - *

Example Usage

- *
-     * Messager messager = processingEnv.getMessager();
-     * printMessage(messager, Kind.ERROR, "Failed to process {0} elements", count);
-     * 
- * - * @param messager the messager to use for printing the message - * @param kind the kind of message to print (e.g., error, warning, note) - * @param pattern the message pattern to format (supports {@link String#format} syntax) - * @param args the arguments for the message pattern - */ - static void printMessage(Messager messager, Kind kind, String pattern, Object... args) { - String message = format(pattern, args); - messager.printMessage(kind, message); - switch (kind) { - case ERROR: - error(pattern, args); - break; - case WARNING: - case MANDATORY_WARNING: - warn(pattern, args); - break; - case NOTE: - info(pattern, args); - break; - default: - debug(pattern, args); - } - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MethodUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MethodUtils.java deleted file mode 100644 index 510f73f59..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/MethodUtils.java +++ /dev/null @@ -1,1165 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - - -import io.microsphere.annotation.Immutable; -import io.microsphere.annotation.Nonnull; -import io.microsphere.annotation.Nullable; -import io.microsphere.util.Utils; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import java.lang.reflect.Type; -import java.util.List; -import java.util.Objects; -import java.util.function.Predicate; - -import static io.microsphere.annotation.processor.util.ElementUtils.filterElements; -import static io.microsphere.annotation.processor.util.ElementUtils.isPublicNonStatic; -import static io.microsphere.annotation.processor.util.ElementUtils.matchParameterTypeNames; -import static io.microsphere.annotation.processor.util.ElementUtils.matchParameterTypes; -import static io.microsphere.annotation.processor.util.MemberUtils.getDeclaredMembers; -import static io.microsphere.annotation.processor.util.TypeUtils.isSameType; -import static io.microsphere.annotation.processor.util.TypeUtils.ofDeclaredType; -import static io.microsphere.collection.CollectionUtils.isEmpty; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; -import static io.microsphere.lang.function.Predicates.and; -import static io.microsphere.lang.function.Streams.filterFirst; -import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; -import static io.microsphere.util.ArrayUtils.EMPTY_TYPE_ARRAY; -import static io.microsphere.util.ArrayUtils.isNotEmpty; -import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableList; -import static java.util.stream.Collectors.toList; -import static javax.lang.model.element.ElementKind.METHOD; -import static javax.lang.model.util.ElementFilter.methodsIn; - -/** - * The utilities class for method in the package "javax.lang.model." - * - * @author Mercy - * @since 1.0.0 - */ -public interface MethodUtils extends Utils { - - /** - * Gets all declared methods of the specified {@link TypeElement}. - * - *

This method returns a list of methods directly declared in the given type element, - * excluding inherited methods. If the provided type element is {@code null}, an empty list is returned. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * List methods = getDeclaredMethods(typeElement);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type element, may be null - * @return a list of executable elements representing all declared methods of the specified type, - * or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List getDeclaredMethods(TypeElement type) { - return findDeclaredMethods(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Gets all declared methods of the specified {@link TypeMirror}. - * - *

This method returns a list of methods directly declared in the given type mirror, - * excluding inherited methods. If the provided type mirror is {@code null}, an empty list is returned. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * List methods = getDeclaredMethods(typeMirror);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type mirror, may be null - * @return a list of executable elements representing all declared methods of the specified type, - * or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List getDeclaredMethods(TypeMirror type) { - return findDeclaredMethods(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Get all declared methods of the specified type element, including those inherited from superclasses and interfaces. - * - *

This method returns a list of methods directly declared in the given type element, including those - * inherited from superclasses and interfaces. If the provided type element is {@code null}, an empty list is returned. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * List methods = getAllDeclaredMethods(typeElement);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type element, may be null - * @return a list of executable elements representing all declared methods of the specified type, - * including those inherited from superclasses and interfaces, or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List getAllDeclaredMethods(TypeElement type) { - return findAllDeclaredMethods(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Get all declared methods of the specified type mirror, including those inherited from superclasses and interfaces. - * - *

This method returns a list of methods directly declared in the given type mirror, including those - * inherited from superclasses and interfaces. If the provided type mirror is {@code null}, an empty list is returned. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * List methods = getAllDeclaredMethods(typeMirror);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type mirror, may be null - * @return a list of executable elements representing all declared methods of the specified type, - * including those inherited from superclasses and interfaces, or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List getAllDeclaredMethods(TypeMirror type) { - return findAllDeclaredMethods(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Find the declared methods of the specified {@link TypeElement}. - * - *

This method returns a list of methods directly declared in the given type element, - * excluding inherited methods. If the provided type element is {@code null}, an empty list is returned. - * Additional filters can be applied to narrow down the list of methods based on custom criteria. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * List methods = findDeclaredMethods(typeElement);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method: " + method.getSimpleName());
-     * }
-     * }
- * - *

Filtering usage example: - *

{@code
-     * List publicNonStaticMethods = findDeclaredMethods(typeElement, MethodUtils::isPublicNonStaticMethod);
-     * for (ExecutableElement method : publicNonStaticMethods) {
-     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type element, may be null - * @param methodFilters the filters for method elements - * @return a list of executable elements representing all declared methods of the specified type, - * or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List findDeclaredMethods(TypeElement type, Predicate... methodFilters) { - return type == null ? emptyList() : findDeclaredMethods(type.asType(), methodFilters); - } - - /** - * Find the declared methods of the specified type mirror. - * - *

This method returns a list of methods directly declared in the given type mirror, - * excluding inherited methods. If the provided type mirror is {@code null}, an empty list is returned. - * Additional filters can be applied to narrow down the list of methods based on custom criteria. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * List methods = findDeclaredMethods(typeMirror);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method: " + method.getSimpleName());
-     * }
-     * }
- * - *

Filtering usage example: - *

{@code
-     * List publicNonStaticMethods = findDeclaredMethods(typeMirror, MethodUtils::isPublicNonStaticMethod);
-     * for (ExecutableElement method : publicNonStaticMethods) {
-     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type mirror, may be null - * @param methodFilters the filters for method elements - * @return a list of executable elements representing all declared methods of the specified type, - * or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List findDeclaredMethods(TypeMirror type, Predicate... methodFilters) { - return filterDeclaredMethods(type, false, methodFilters); - } - - /** - * Find all declared methods of the specified type element, including those inherited from superclasses and interfaces, - * and exclude methods declared in the specified excluded types. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * List methods = findAllDeclaredMethods(typeElement);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
-     * }
-     * }
- * - *

Excluding methods from specific types: - *

{@code
-     * List methodsExcludingObject = findAllDeclaredMethods(typeElement, Object.class);
-     * for (ExecutableElement method : methodsExcludingObject) {
-     *     System.out.println("Method excluding Object: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type element, may be null - * @param excludedTypes the types whose methods should be excluded from the result, optional - * @return a list of executable elements representing all declared methods of the specified type, - * including those inherited from superclasses and interfaces, but excluding those declared - * in the excluded types, or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List findAllDeclaredMethods(TypeElement type, Type... excludedTypes) { - return type == null ? emptyList() : findAllDeclaredMethods(type.asType(), excludedTypes); - } - - /** - * Find all declared methods of the specified type mirror, including those inherited from superclasses and interfaces, - * and exclude methods declared in the specified excluded types. - * - *

This method returns a list of methods directly declared in the given type mirror, - * including those inherited from superclasses and interfaces. If the provided type mirror is {@code null}, - * an empty list is returned. Additional filtering can be applied to exclude methods declared in specific types. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * List methods = findAllDeclaredMethods(typeMirror);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
-     * }
-     * }
- * - *

Excluding methods from specific types: - *

{@code
-     * List methodsExcludingObject = findAllDeclaredMethods(typeMirror, Object.class);
-     * for (ExecutableElement method : methodsExcludingObject) {
-     *     System.out.println("Method excluding Object: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type mirror, may be null - * @param excludedTypes the types whose methods should be excluded from the result, optional - * @return a list of executable elements representing all declared methods of the specified type, - * excluding those declared in the excluded types, or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List findAllDeclaredMethods(TypeMirror type, Type... excludedTypes) { - if (type == null) { - return emptyList(); - } - return findAllDeclaredMethods(type, methodPredicateForExcludedTypes(excludedTypes)); - } - - /** - * Finds all public non-static methods declared in the specified {@link TypeElement}, excluding those inherited from superclasses or interfaces, - * and optionally excludes methods declared in the specified excluded types. - * - *

This method returns a list of executable elements representing public non-static methods directly declared in the given type element. - * If the provided type element is {@code null}, an empty list is returned. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * List methods = findPublicNonStaticMethods(typeElement);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
-     * }
-     * }
- * - *

Excluding methods from specific types: - *

{@code
-     * List methodsExcludingObject = findPublicNonStaticMethods(typeElement, Object.class);
-     * for (ExecutableElement method : methodsExcludingObject) {
-     *     System.out.println("Public Non-Static Method (excluding Object): " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type element, may be null - * @param excludedTypes the types whose methods should be excluded from the result, optional - * @return a list of executable elements representing all public non-static methods declared in the specified type, - * or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List findPublicNonStaticMethods(TypeElement type, Type... excludedTypes) { - return type == null ? emptyList() : findPublicNonStaticMethods(ofDeclaredType(type), excludedTypes); - } - - /** - * Find all public non-static methods declared in the specified type mirror, excluding those inherited from superclasses or interfaces, - * and optionally exclude methods declared in the specified excluded types. - * - *

This method returns a list of executable elements representing public non-static methods directly declared in the given type. - * If the provided type is {@code null}, an empty list is returned. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * List methods = findPublicNonStaticMethods(typeMirror);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
-     * }
-     * }
- * - *

Excluding methods from specific types: - *

{@code
-     * List methodsExcludingObject = findPublicNonStaticMethods(typeMirror, Object.class);
-     * for (ExecutableElement method : methodsExcludingObject) {
-     *     System.out.println("Public Non-Static Method (excluding Object): " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type mirror, may be null - * @param excludedTypes the types whose methods should be excluded from the result, optional - * @return a list of executable elements representing all public non-static methods declared in the specified type, - * excluding those declared in the excluded types, or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List findPublicNonStaticMethods(TypeMirror type, Type... excludedTypes) { - if (type == null) { - return emptyList(); - } - - Predicate predicate = and(methodPredicateForExcludedTypes(excludedTypes), MethodUtils::isPublicNonStaticMethod); - - return findAllDeclaredMethods(type, predicate); - } - - /** - * Find all declared methods of the specified {@link TypeElement}, including those inherited from superclasses and interfaces, - * and optionally filter them using the provided predicates. - * - *

This method returns a list of methods directly declared in the given type element, - * including those inherited from superclasses and interfaces. If the provided type element is {@code null}, - * an empty list is returned. Additional filters can be applied to narrow down the list of methods based on custom criteria. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * List methods = findAllDeclaredMethods(typeElement);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
-     * }
-     * }
- * - *

Filtering usage example: - *

{@code
-     * List publicNonStaticMethods = findAllDeclaredMethods(typeElement, MethodUtils::isPublicNonStaticMethod);
-     * for (ExecutableElement method : publicNonStaticMethods) {
-     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type element, may be null - * @param methodFilters the filters for method elements, optional - * @return a list of executable elements representing all declared methods of the specified type, - * including those inherited from superclasses and interfaces, or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List findAllDeclaredMethods(TypeElement type, Predicate... methodFilters) { - return type == null ? emptyList() : findAllDeclaredMethods(type.asType(), methodFilters); - } - - /** - * Finds all declared methods of the specified type mirror, including those inherited from superclasses and interfaces, - * and optionally filters them using the provided predicates. - * - *

This method returns a list of methods directly declared in the given type mirror, including those - * inherited from superclasses and interfaces. If the provided type mirror is {@code null}, an empty list is returned. - * Additional filters can be applied to narrow down the list of methods based on custom criteria. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * List methods = findAllDeclaredMethods(typeMirror);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
-     * }
-     * }
- * - *

Filtering usage example: - *

{@code
-     * List publicNonStaticMethods = findAllDeclaredMethods(typeMirror, MethodUtils::isPublicNonStaticMethod);
-     * for (ExecutableElement method : publicNonStaticMethods) {
-     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the specified type mirror, may be null - * @param methodFilters the filters for method elements, optional - * @return a list of executable elements representing all declared methods of the specified type, - * including those inherited from superclasses and interfaces, or an empty list if the input type is null - */ - @Nonnull - @Immutable - static List findAllDeclaredMethods(TypeMirror type, Predicate... methodFilters) { - return filterDeclaredMethods(type, true, methodFilters); - } - - /** - * Filters the declared methods of the specified type based on the given predicates. - * - *

This method returns a list of methods directly declared in the given type, - * optionally including those inherited from superclasses and interfaces. If the provided type is {@code null}, - * an empty list is returned. Additional filters can be applied to narrow down the list of methods based on custom criteria. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * List methods = filterDeclaredMethods(typeMirror, false);
-     * for (ExecutableElement method : methods) {
-     *     System.out.println("Declared Method: " + method.getSimpleName());
-     * }
-     * }
- * - *

Filtering usage example: - *

{@code
-     * List publicNonStaticMethods = filterDeclaredMethods(typeMirror, false, MethodUtils::isPublicNonStaticMethod);
-     * for (ExecutableElement method : publicNonStaticMethods) {
-     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the type whose declared methods are to be filtered, may be null - * @param includeHierarchicalTypes whether to include methods from superclasses and interfaces - * @param methodFilters the predicates used to filter the methods, optional - * @return a list of executable elements representing the filtered methods, - * or an empty list if the input type is null or no methods match the filters - */ - @Nonnull - @Immutable - static List filterDeclaredMethods(TypeMirror type, boolean includeHierarchicalTypes, Predicate... methodFilters) { - if (type == null) { - return emptyList(); - } - - List declaredMembers = getDeclaredMembers(type, includeHierarchicalTypes); - if (isEmpty(declaredMembers)) { - return emptyList(); - } - - List methods = methodsIn(declaredMembers); - - return filterElements(methods, methodFilters); - } - - /** - * Checks if the given executable element is a method. - * - *

This method determines whether the provided executable element represents a method. - * If the element is {@code null}, the method returns {@code false}. - * - *

Example Usage

- *
{@code
-     * ExecutableElement executableElement = ...; // Obtain a valid ExecutableElement
-     * boolean isMethod = MethodUtils.isMethod(executableElement);
-     * if (isMethod) {
-     *     System.out.println("The element is a method.");
-     * } else {
-     *     System.out.println("The element is not a method.");
-     * }
-     * }
- * - * @param method the executable element to check, may be null - * @return true if the element is a method, false otherwise - */ - static boolean isMethod(ExecutableElement method) { - return method != null && METHOD.equals(method.getKind()); - } - - /** - * Checks whether the given method is a public non-static method. - * - *

This method verifies if the provided executable element represents a method that is both public and non-static. - * If the method is {@code null}, the method returns {@code false}. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
-     * boolean isPublicNonStatic = MethodUtils.isPublicNonStaticMethod(method);
-     * if (isPublicNonStatic) {
-     *     System.out.println("The method is a public non-static method.");
-     * } else {
-     *     System.out.println("The method is not a public non-static method.");
-     * }
-     * }
- * - * @param method the executable element to check, may be null - * @return true if the method is a public non-static method, false otherwise - */ - static boolean isPublicNonStaticMethod(ExecutableElement method) { - return isMethod(method) && isPublicNonStatic(method); - } - - /** - * Finds a method with the specified name in the given type element, using an empty parameter type array as default. - * - *

This method searches for a method with the specified name in the given type element. - * If no method with the specified name is found, it returns {@code null}. - * The search considers methods declared directly in the type, excluding inherited methods. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * String methodName = "myMethod";
-     * ExecutableElement method = MethodUtils.findMethod(typeElement, methodName);
-     *
-     * if (method != null) {
-     *     System.out.println("Found method: " + method.getSimpleName());
-     * } else {
-     *     System.out.println("Method not found.");
-     * }
-     * }
- * - * @param type the type element to search for the method - * @param methodName the name of the method to find - * @return the first matching executable element representing the method, or null if none is found - */ - @Nullable - static ExecutableElement findMethod(TypeElement type, String methodName) { - return findMethod(type, methodName, EMPTY_TYPE_ARRAY); - } - - /** - * Finds a method with the specified name in the given type mirror, using an empty parameter type array as default. - * - *

This method searches for a method with the specified name in the given type mirror. - * If no method with the specified name is found, it returns {@code null}. - * The search considers methods declared directly in the type, excluding inherited methods. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * String methodName = "myMethod";
-     * ExecutableElement method = MethodUtils.findMethod(typeMirror, methodName);
-     *
-     * if (method != null) {
-     *     System.out.println("Found method: " + method.getSimpleName());
-     * } else {
-     *     System.out.println("Method not found.");
-     * }
-     * }
- * - * @param type the type mirror to search for the method - * @param methodName the name of the method to find - * @return the first matching executable element representing the method, or null if none is found - */ - @Nullable - static ExecutableElement findMethod(TypeMirror type, String methodName) { - return findMethod(type, methodName, EMPTY_TYPE_ARRAY); - } - - /** - * Finds a method with the specified name and parameter types in the given {@link TypeElement}. - * - *

This method searches for a method with the specified name and exact parameter types - * directly declared in the provided type element, excluding inherited methods. - * If no matching method is found, it returns {@code null}. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * String methodName = "myMethod";
-     * Type[] parameterTypes = new Type[] { String.class, int.class };
-     * ExecutableElement method = MethodUtils.findMethod(typeElement, methodName, parameterTypes);
-     *
-     * if (method != null) {
-     *     System.out.println("Found method: " + method.getSimpleName());
-     * } else {
-     *     System.out.println("Method not found.");
-     * }
-     * }
- * - * @param type the specified type element, may be null - * @param methodName the name of the method to find, must not be null - * @param parameterTypes the parameter types of the method to match, must not be null - * @return the first matching executable element representing the method, or null if none is found - */ - @Nullable - static ExecutableElement findMethod(TypeElement type, String methodName, Type... parameterTypes) { - return type == null ? null : findMethod(type.asType(), methodName, parameterTypes); - } - - /** - * Finds a method with the specified name and parameter types in the given type mirror. - * - *

This method searches for a method with the specified name and exact parameter types - * directly declared in the provided type mirror, excluding inherited methods. - * If no matching method is found, it returns {@code null}. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * String methodName = "myMethod";
-     * Type[] parameterTypes = new Type[] { String.class, int.class };
-     * ExecutableElement method = MethodUtils.findMethod(typeMirror, methodName, parameterTypes);
-     *
-     * if (method != null) {
-     *     System.out.println("Found method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the type mirror to search for the method, may be null - * @param methodName the name of the method to find, must not be null - * @param parameterTypes the parameter types of the method to match, must not be null - * @return the first matching executable element representing the method, or null if none is found - */ - @Nullable - static ExecutableElement findMethod(TypeMirror type, String methodName, Type... parameterTypes) { - if (type == null || methodName == null || parameterTypes == null) { - return null; - } - List allDeclaredMethods = findAllDeclaredMethods(type, method -> matches(method, methodName, parameterTypes)); - return allDeclaredMethods.isEmpty() ? null : allDeclaredMethods.get(0); - } - - /** - * Finds a method with the specified name and parameter type names in the given {@link TypeElement}. - * - *

This method searches for a method with the specified name and exact parameter type names - * directly declared in the provided type element, excluding inherited methods. - * If no matching method is found, it returns {@code null}. - * - *

Example Usage

- *
{@code
-     * TypeElement typeElement = ...; // Obtain a valid TypeElement
-     * String methodName = "myMethod";
-     * CharSequence[] paramTypeNames = new CharSequence[] { "java.lang.String", "int" };
-     * ExecutableElement method = MethodUtils.findMethod(typeElement, methodName, paramTypeNames);
-     *
-     * if (method != null) {
-     *     System.out.println("Found method: " + method.getSimpleName());
-     * } else {
-     *     System.out.println("Method not found.");
-     * }
-     * }
- * - * @param type the type element to search for the method, may be null - * @param methodName the name of the method to find, must not be null - * @param parameterTypeNames the names of the parameter types of the method to match, must not be null - * @return the first matching executable element representing the method, or null if none is found - */ - @Nullable - static ExecutableElement findMethod(TypeElement type, String methodName, CharSequence... parameterTypeNames) { - return type == null ? null : findMethod(type.asType(), methodName, parameterTypeNames); - } - - /** - * Finds a method with the specified name and parameter type names in the given type mirror. - * - *

This method searches for a method with the specified name and exact parameter type names - * directly declared in the provided type mirror, excluding inherited methods. - * If no matching method is found, it returns {@code null}. - * - *

Example Usage

- *
{@code
-     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
-     * String methodName = "myMethod";
-     * CharSequence[] paramTypeNames = new CharSequence[] { "java.lang.String", "int" };
-     * ExecutableElement method = MethodUtils.findMethod(typeMirror, methodName, paramTypeNames);
-     *
-     * if (method != null) {
-     *     System.out.println("Found method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param type the type mirror to search for the method, may be null - * @param methodName the name of the method to find, must not be null - * @param parameterTypeNames the names of the parameter types of the method to match, must not be null - * @return the first matching executable element representing the method, or null if none is found - */ - @Nullable - static ExecutableElement findMethod(TypeMirror type, String methodName, CharSequence... parameterTypeNames) { - if (type == null || methodName == null || parameterTypeNames == null) { - return null; - } - List allDeclaredMethods = findAllDeclaredMethods(type, method -> matches(method, methodName, parameterTypeNames)); - return allDeclaredMethods.isEmpty() ? null : allDeclaredMethods.get(0); - } - - /** - * Finds the overridden method in the specified type that corresponds to the given declaring method. - * - *

This method searches for a method in the provided type element that overrides the specified - * declaring method. It utilizes the processing environment to determine method overriding using - * the {@link Elements#overrides(ExecutableElement, ExecutableElement, TypeElement)} method. - * - *

Example Usage

- *
{@code
-     * ProcessingEnvironment processingEnv = ...; // Obtain a valid ProcessingEnvironment
-     * TypeElement typeElement = ...; // The type in which to find the overridden method
-     * ExecutableElement declaringMethod = ...; // The method being overridden
-     *
-     * ExecutableElement overriddenMethod = getOverrideMethod(processingEnv, typeElement, declaringMethod);
-     *
-     * if (overriddenMethod != null) {
-     *     System.out.println("Overridden Method Found: " + overriddenMethod.getSimpleName());
-     * } else {
-     *     System.out.println("No overridden method found.");
-     * }
-     * }
- * - * @param processingEnv the processing environment used to determine overriding - * @param type the type element in which to search for the overridden method - * @param declaringMethod the method element whose override is to be found - * @return the overridden method in the specified type, or null if no such method exists - */ - @Nullable - static ExecutableElement getOverrideMethod(ProcessingEnvironment processingEnv, TypeElement type, ExecutableElement declaringMethod) { - Elements elements = processingEnv.getElementUtils(); - return filterFirst(getAllDeclaredMethods(type), method -> elements.overrides(method, declaringMethod, type)); - } - - /** - * Filters the given list of executable elements (methods) based on the provided predicates. - * - *

This method applies a set of filtering predicates to a list of methods and returns a new list - * containing only the methods that satisfy all the provided conditions. If no method matches, - * an empty list is returned. If no filters are provided, the original list is returned unchanged. - * - *

Example Usage

- *
{@code
-     * List methods = MethodUtils.getDeclaredMethods(typeElement);
-     * List publicNonStaticMethods = MethodUtils.filterMethods(methods, MethodUtils::isPublicNonStaticMethod);
-     *
-     * for (ExecutableElement method : publicNonStaticMethods) {
-     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
-     * }
-     * }
- * - *

Multiple filters can be combined: - *

{@code
-     * Predicate isPublic = method -> method.getModifiers().contains(Modifier.PUBLIC);
-     * Predicate isVoidReturnType = method -> "void".equals(TypeUtils.toString(method.getReturnType()));
-     *
-     * List filteredMethods = MethodUtils.filterMethods(methods, isPublic, isVoidReturnType);
-     * for (ExecutableElement method : filteredMethods) {
-     *     System.out.println("Public Method with Void Return Type: " + method.getSimpleName());
-     * }
-     * }
- * - * @param methods the list of executable elements (methods) to be filtered - * @param methodFilters the array of predicates used for filtering the methods - * @return a filtered list of executable elements matching all the provided predicates, - * or an empty list if the input list is null or empty, or no methods match the filters - */ - @Nonnull - @Immutable - static List filterMethods(List methods, Predicate... methodFilters) { - if (isEmpty(methods)) { - return emptyList(); - } - - List filteredMethods = methods; - if (isNotEmpty(methodFilters)) { - Predicate combinedPredicate = and(methodFilters); - filteredMethods = methods.stream() - .filter(combinedPredicate) - .collect(toList()); - } - - return filteredMethods.isEmpty() ? emptyList() : unmodifiableList(filteredMethods); - } - - /** - * Gets the simple name of the method as a string. - * - *

If the provided method is null, this method returns null. - * Otherwise, it returns the simple name of the method as a string. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement representing a method
-     * String methodName = MethodUtils.getMethodName(method);
-     * if (methodName != null) {
-     *     System.out.println("Method Name: " + methodName);
-     * } else {
-     *     System.out.println("Method is null.");
-     * }
-     * }
- * - * @param method the executable element representing the method, may be null - * @return the simple name of the method as a string, or null if the method is null - */ - @Nullable - static String getMethodName(ExecutableElement method) { - return method == null ? null : method.getSimpleName().toString(); - } - - /** - * Gets the return type name of the given method. - * - *

If the provided method is null, this method returns null. - * Otherwise, it returns the fully qualified name of the method's return type. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement representing a method
-     * String returnTypeName = MethodUtils.getReturnTypeName(method);
-     * if (returnTypeName != null) {
-     *     System.out.println("Return Type Name: " + returnTypeName);
-     * } else {
-     *     System.out.println("Method is null or has no return type.");
-     * }
-     * }
- * - * @param method the executable element representing the method, may be null - * @return the fully qualified name of the method's return type as a string, or null if the method is null - */ - @Nullable - static String getReturnTypeName(ExecutableElement method) { - return method == null ? null : TypeUtils.toString(method.getReturnType()); - } - - /** - * Gets the parameter type mirrors of the given method. - * - *

This method returns a list of type mirrors representing the parameter types of the provided method. - * If the method is {@code null} or has no parameters, an empty list is returned. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement representing a method
-     * List parameterTypeMirrors = MethodUtils.getMethodParameterTypeMirrors(method);
-     *
-     * if (!parameterTypeMirrors.isEmpty()) {
-     *     for (TypeMirror typeMirror : parameterTypeMirrors) {
-     *         System.out.println("Parameter Type: " + TypeUtils.toString(typeMirror));
-     *     }
-     * } else {
-     *     System.out.println("Method is null or has no parameters.");
-     * }
-     * }
- * - * @param method the executable element representing the method, may be null - * @return a list of type mirrors representing the parameter types of the method, - * or an empty list if the method is null or has no parameters - */ - @Nonnull - @Immutable - static List getMethodParameterTypeMirrors(ExecutableElement method) { - if (method == null) { - return emptyList(); - } - - List parameters = method.getParameters(); - if (parameters.isEmpty()) { - return emptyList(); - } - - List parameterTypes = parameters.stream() - .map(VariableElement::asType) - .collect(toList()); - - return unmodifiableList(parameterTypes); - } - - /** - * Gets the parameter type names of the given method. - * - *

This method returns an array of strings representing the fully qualified names - * of the parameter types of the provided method. If the method is {@code null} or has no parameters, - * an empty array is returned. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement representing a method
-     * String[] parameterTypeNames = MethodUtils.getMethodParameterTypeNames(method);
-     *
-     * if (parameterTypeNames.length > 0) {
-     *     for (String typeName : parameterTypeNames) {
-     *         System.out.println("Parameter Type: " + typeName);
-     *     }
-     * } else {
-     *     System.out.println("Method is null or has no parameters.");
-     * }
-     * }
- * - * @param method the executable element representing the method, may be null - * @return an array of strings representing the fully qualified names of the parameter types, - * or an empty array if the method is null or has no parameters - */ - static String[] getMethodParameterTypeNames(ExecutableElement method) { - List parameterTypes = getMethodParameterTypeMirrors(method); - return parameterTypes.isEmpty() ? EMPTY_STRING_ARRAY : - parameterTypes.stream().map(TypeUtils::toString).toArray(String[]::new); - } - - /** - * Checks if the given method matches the specified method name and parameter types. - * - *

This method determines whether the provided executable element (method) has the same name - * and parameter types as specified. If the method is null or any of the parameters are null, - * the result will be false. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
-     * boolean isMatch = matches(method, "myMethod", String.class, int.class);
-     * if (isMatch) {
-     *     System.out.println("The method matches the specified name and parameter types.");
-     * } else {
-     *     System.out.println("The method does not match.");
-     * }
-     * }
- * - * @param method the executable element representing the method to check - * @param methodName the name of the method to match - * @param parameterTypes the parameter types of the method to match - * @return true if the method matches the given name and parameter types, false otherwise - */ - static boolean matches(ExecutableElement method, String methodName, Type... parameterTypes) { - return matchesMethod(method, methodName, parameterTypes); - } - - /** - * Checks if the given method matches the specified method name and parameter type names. - * - *

This method determines whether the provided executable element (method) has the same name - * and parameter type names as specified. If the method is null or any of the parameters are null, - * the result will be false. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
-     * boolean isMatch = matches(method, "myMethod", "java.lang.String", "int");
-     * if (isMatch) {
-     *     System.out.println("The method matches the specified name and parameter type names.");
-     * } else {
-     *     System.out.println("The method does not match.");
-     * }
-     * }
- * - * @param method the executable element representing the method to check - * @param methodName the name of the method to match - * @param parameterTypeNames the names of the parameter types to match - * @return true if the method matches the given name and parameter type names, false otherwise - */ - static boolean matches(ExecutableElement method, String methodName, CharSequence... parameterTypeNames) { - return matchesMethod(method, methodName, parameterTypeNames); - } - - /** - * Checks if the given method has the specified method name. - * - *

This method compares the simple name of the provided executable element (method) - * with the given method name. If either the method or the method name is {@code null}, - * the comparison is performed using {@link Objects#equals(Object, Object)}. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
-     * boolean isMatch = MethodUtils.matchesMethodName(method, "myMethod");
-     *
-     * if (isMatch) {
-     *     System.out.println("The method name matches.");
-     * } else {
-     *     System.out.println("The method name does not match.");
-     * }
-     * }
- * - * @param method the executable element representing the method to check - * @param methodName the name of the method to match, may be null - * @return true if the method's name matches the given method name, false otherwise - */ - static boolean matchesMethodName(ExecutableElement method, String methodName) { - return Objects.equals(getMethodName(method), methodName); - } - - /** - * Checks if the given method matches the specified method name and parameter types. - * - *

This method determines whether the provided executable element (method) has the same name - * and parameter types as specified. If the method is null or any of the parameters are null, - * the result will be false. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
-     * boolean isMatch = MethodUtils.matchesMethod(method, "myMethod", String.class, int.class);
-     * if (isMatch) {
-     *     System.out.println("The method matches the specified name and parameter types.");
-     * } else {
-     *     System.out.println("The method does not match.");
-     * }
-     * }
- * - * @param method the executable element representing the method to check - * @param methodName the name of the method to match - * @param parameterTypes the parameter types of the method to match - * @return true if the method matches the given name and parameter types, false otherwise - */ - static boolean matchesMethod(ExecutableElement method, String methodName, Type... parameterTypes) { - if (method == null || methodName == null || parameterTypes == null) { - return false; - } - - // Check if the method name matches - if (!matchesMethodName(method, methodName)) { - return false; - } - - // Check if the parameter types match - if (!matchParameterTypes(method, parameterTypes)) { - return false; - } - - return true; - } - - /** - * Checks if the given method matches the specified method name and parameter type names. - * - *

This method determines whether the provided executable element (method) has the same name - * and parameter type names as specified. If the method is null or any of the parameters are null, - * the result will be false. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
-     * boolean isMatch = MethodUtils.matchesMethod(method, "myMethod", "java.lang.String", "int");
-     * if (isMatch) {
-     *     System.out.println("The method matches the specified name and parameter type names.");
-     * } else {
-     *     System.out.println("The method does not match.");
-     * }
-     * }
- * - * @param method the executable element representing the method to check - * @param methodName the name of the method to match - * @param parameterTypeNames the names of the parameter types to match - * @return true if the method matches the given name and parameter type names, false otherwise - */ - static boolean matchesMethod(ExecutableElement method, String methodName, CharSequence... parameterTypeNames) { - if (method == null || methodName == null || parameterTypeNames == null) { - return false; - } - - // matches the name of method - if (!Objects.equals(getMethodName(method), methodName)) { - return false; - } - - if (!matchParameterTypeNames(method.getParameters(), parameterTypeNames)) { - return false; - } - - return true; - } - - /** - * Returns the enclosing element of the given executable method. - * - *

This method retrieves the element that directly encloses the provided method. - * If the method is {@code null}, the method returns {@code null}. - * - *

Example Usage

- *
{@code
-     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
-     * Element enclosingElement = MethodUtils.getEnclosingElement(method);
-     * if (enclosingElement != null) {
-     *     System.out.println("Enclosing Element: " + enclosingElement.getSimpleName());
-     * } else {
-     *     System.out.println("Method is null or has no enclosing element.");
-     * }
-     * }
- * - * @param method the executable element representing the method, may be null - * @return the enclosing element of the method, or null if the method is null - */ - @Nullable - static Element getEnclosingElement(ExecutableElement method) { - return method == null ? null : method.getEnclosingElement(); - } - - /** - * Creates a predicate that filters out methods declared in the specified excluded types. - * - *

This predicate can be used to exclude methods that are declared in certain types, - * such as standard Java types like {@link Object}, when searching or filtering through methods. - * If no excluded types are provided, the predicate will allow all methods. - * - *

Example Usage

- *
{@code
-     * Predicate excludeObjectMethods = methodPredicateForExcludedTypes(Object.class);
-     * List filteredMethods = findDeclaredMethods(typeMirror, excludeObjectMethods);
-     *
-     * for (ExecutableElement method : filteredMethods) {
-     *     System.out.println("Filtered Method: " + method.getSimpleName());
-     * }
-     * }
- * - * @param excludedTypes the types whose methods should be excluded from the result, optional - * @return a predicate that returns {@code true} for methods not declared in any of the excluded types - */ - @Nonnull - static Predicate methodPredicateForExcludedTypes(Type... excludedTypes) { - return method -> { - boolean excluded = true; - Element declaredType = getEnclosingElement(method); - for (Type excludedType : excludedTypes) { - if (isSameType(declaredType, excludedType)) { - excluded = false; - break; - } - } - return excluded; - }; - } -} diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/TypeUtils.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/TypeUtils.java deleted file mode 100644 index 829ed464c..000000000 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/util/TypeUtils.java +++ /dev/null @@ -1,2505 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.Immutable; -import io.microsphere.annotation.Nonnull; -import io.microsphere.annotation.Nullable; -import io.microsphere.util.TypeFinder; -import io.microsphere.util.Utils; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.TypeParameterElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; -import java.util.function.Predicate; - -import static io.microsphere.collection.CollectionUtils.isEmpty; -import static io.microsphere.collection.Lists.ofList; -import static io.microsphere.constants.SymbolConstants.GREATER_THAN_CHAR; -import static io.microsphere.constants.SymbolConstants.LESS_THAN_CHAR; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; -import static io.microsphere.lang.function.Predicates.and; -import static io.microsphere.lang.function.Streams.filterFirst; -import static io.microsphere.reflect.MethodUtils.invokeMethod; -import static io.microsphere.reflect.TypeUtils.getTypeNames; -import static io.microsphere.util.ArrayUtils.contains; -import static io.microsphere.util.ArrayUtils.isEmpty; -import static io.microsphere.util.Assert.assertNoNullElements; -import static io.microsphere.util.ClassUtils.SIMPLE_TYPES; -import static java.lang.String.valueOf; -import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableList; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Stream.of; -import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE; -import static javax.lang.model.element.ElementKind.CLASS; -import static javax.lang.model.element.ElementKind.ENUM; -import static javax.lang.model.element.ElementKind.INTERFACE; -import static javax.lang.model.type.TypeKind.ARRAY; - -/** - * The utilities class for type in the package "javax.lang.model.*" - * - * @author
Mercy - * @since 1.0.0 - */ -public interface TypeUtils extends Utils { - - /** - * A list of names representing simple types in Java. - * Simple types include primitive types, void, and commonly used basic classes like String, Number, etc. - */ - @Nonnull - @Immutable - List SIMPLE_TYPE_NAMES = ofList( - SIMPLE_TYPES - .stream() - .map(Class::getName) - .toArray(String[]::new) - ); - - /** - * Get the superclass of the specified type - */ - @Nonnull - Function TYPE_ELEMENT_GET_SUPERCLASS = type -> ofTypeElement(type.getSuperclass()); - - /** - * Get the interfaces of the specified type - */ - @Nonnull - Function TYPE_ELEMENT_GET_INTERFACES = type -> type.getInterfaces() - .stream() - .map(TypeUtils::ofTypeElement) - .toArray(TypeElement[]::new); - - - /** - * Checks if the given Element represents a simple type. - * A simple type is defined as a basic data type that can be directly represented without further resolution. - * - *

- * Examples of simple types include primitive types (e.g., int, boolean), - * built-in types like void, and commonly used basic classes like String or Number. - *

- * - *

Example Usage

- *
{@code
-     * Element intElement = ...; // represents 'int'
-     * boolean isSimple = TypeUtils.isSimpleType(intElement); // returns true
-     *
-     * Element customClassElement = ...; // represents a custom class like 'MyClass'
-     * boolean isSimple = TypeUtils.isSimpleType(customClassElement); // returns false
-     * }
- * - * @param element the element to check, may be null - * @return true if the element is a simple type, false otherwise - */ - static boolean isSimpleType(Element element) { - return element != null && isSimpleType(element.asType()); - } - - /** - * Checks whether the given {@link TypeMirror} represents a simple type. - * A simple type is one that can be directly represented without further resolution, - * such as primitive types, built-in types like void, or commonly recognized basic types. - * - *

- * Examples of simple types include: - *

    - *
  • Primitive types: int, boolean, char, etc.
  • - *
  • Built-in types: void, java.lang.String, java.lang.Number
  • - *
- *

- * - *

Example Usage

- *
{@code
-     * TypeMirror intType = ...; // represents 'int'
-     * boolean isSimple = TypeUtils.isSimpleType(intType); // returns true
-     *
-     * TypeMirror customType = ...; // represents a custom class like 'MyClass'
-     * boolean isSimple = TypeUtils.isSimpleType(customType); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the type is a simple type; false otherwise - */ - static boolean isSimpleType(TypeMirror type) { - return type != null && SIMPLE_TYPE_NAMES.contains(type.toString()); - } - - /** - * Checks if the given Element and Type represent the same type. - * - *

- * This method compares the type information of the provided Element and Type objects - * to determine if they represent the same type. If either parameter is null, the comparison - * is made based on whether both are null. - *

- * - *

Example Usage

- *
{@code
-     * Element element = ...; // represents a type like String
-     * Type type = ...; // represents the same type as the element
-     * boolean isSame = TypeUtils.isSameType(element, type); // returns true
-     *
-     * Element element2 = ...; // represents a different type
-     * Type type2 = ...; // represents a different type
-     * boolean isSame2 = TypeUtils.isSameType(element2, type2); // returns false
-     * }
- * - * @param element the Element to compare, may be null - * @param type the Type to compare, may be null - * @return true if both represent the same type; false otherwise - */ - static boolean isSameType(Element element, Type type) { - return isSameType(element == null ? null : element.asType(), type); - } - - /** - * Checks if the given Element and type name represent the same type. - * - *

- * This method compares the type information of the provided Element and the fully qualified class name - * to determine if they represent the same type. If either parameter is null, the comparison - * is made based on whether both are null. - *

- * - *

Example Usage

- *
{@code
-     * Element stringElement = ...; // represents 'java.lang.String'
-     * boolean isSame = TypeUtils.isSameType(stringElement, "java.lang.String"); // returns true
-     *
-     * Element customElement = ...; // represents 'com.example.MyClass'
-     * boolean isSame2 = TypeUtils.isSameType(customElement, "com.example.OtherClass"); // returns false
-     * }
- * - * @param type the Element to compare, may be null - * @param typeName the fully qualified class name to compare, may be null - * @return true if both represent the same type; false otherwise - */ - static boolean isSameType(Element type, CharSequence typeName) { - return isSameType(type == null ? null : type.asType(), typeName); - } - - /** - * Checks if the given {@link TypeMirror} and {@link Type} represent the same type. - * - *

- * This method compares the fully qualified type name of the {@link TypeMirror} with the - * {@link Type#getTypeName() type name} of the provided {@link Type} to determine if they - * represent the same type. If both are {@code null}, they are considered the same. - *

- * - *

Example Usage

- *
{@code
-     * TypeMirror stringTypeMirror = element.asType(); // represents 'java.lang.String'
-     * Type stringType = Class.forName("java.lang.String");
-     * boolean isSame = TypeUtils.isSameType(stringTypeMirror, stringType); // returns true
-     *
-     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
-     * Type intType = int.class;
-     * boolean isSamePrimitive = TypeUtils.isSameType(intTypeMirror, intType); // returns true
-     *
-     * TypeMirror customTypeMirror = ...; // represents 'com.example.MyClass'
-     * Type customType = Class.forName("com.example.OtherClass");
-     * boolean isSameCustom = TypeUtils.isSameType(customTypeMirror, customType); // returns false
-     * }
- * - * @param typeMirror the TypeMirror to compare, may be null - * @param type the Type to compare, may be null - * @return true if both represent the same type; false otherwise - */ - static boolean isSameType(TypeMirror typeMirror, Type type) { - return isSameType(typeMirror, type == null ? null : type.getTypeName()); - } - - /** - * Checks if the given TypeMirror and type name represent the same type. - * - *

- * This method compares the fully qualified type name of the TypeMirror with the provided - * typeName to determine if they represent the same type. If both are null, they are considered - * the same. - *

- * - *

Example Usage

- *
{@code
-     * TypeMirror stringTypeMirror = element.asType(); // represents 'java.lang.String'
-     * boolean isSame = TypeUtils.isSameType(stringTypeMirror, "java.lang.String"); // returns true
-     *
-     * TypeMirror customTypeMirror = ...; // represents 'com.example.MyClass'
-     * boolean isSameCustom = TypeUtils.isSameType(customTypeMirror, "com.example.OtherClass"); // returns false
-     * }
- * - * @param type the TypeMirror to compare, may be null - * @param typeName the fully qualified class name to compare, may be null - * @return true if both represent the same type; false otherwise - */ - static boolean isSameType(TypeMirror type, CharSequence typeName) { - if (type == null && typeName == null) { - return true; - } - return Objects.equals(valueOf(type), valueOf(typeName)); - } - - /** - * Checks whether the given {@link TypeMirror} represents an array type. - * - *

- * This method determines if the provided TypeMirror corresponds to an array type - * by checking its kind against the array type kind defined in the Java language model. - * If the provided TypeMirror is null, the method returns false. - *

- * - *

Example Usage

- *
{@code
-     * TypeMirror arrayTypeMirror = processingEnv.getTypeUtils().getArrayType(...); // represents an array type
-     * boolean isArray = TypeUtils.isArrayType(arrayTypeMirror); // returns true
-     *
-     * TypeMirror stringTypeMirror = element.asType(); // represents 'java.lang.String'
-     * boolean isArray = TypeUtils.isArrayType(stringTypeMirror); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the type is an array type; false otherwise - */ - static boolean isArrayType(TypeMirror type) { - return type != null && ARRAY == type.getKind(); - } - - /** - * Checks whether the given Element represents an array type. - * - *

- * This method determines if the provided Element corresponds to an array type - * by checking its kind against the array type kind defined in the Java language model. - * If the provided Element is null, the method returns false. - *

- * - *

Example Usage

- *
{@code
-     * Element arrayElement = ...; // represents an array type like String[]
-     * boolean isArray = TypeUtils.isArrayType(arrayElement); // returns true
-     *
-     * Element stringElement = ...; // represents 'java.lang.String'
-     * boolean isArray = TypeUtils.isArrayType(stringElement); // returns false
-     * }
- * - * @param element the Element to check, may be null - * @return true if the element represents an array type; false otherwise - */ - static boolean isArrayType(Element element) { - return element != null && isArrayType(element.asType()); - } - - /** - * Checks whether the given {@link TypeMirror} represents an enum type. - * - *

- * This method determines if the provided TypeMirror corresponds to an enum type - * by checking its kind via the underlying element's {@link Element#getKind()}. - * If the provided TypeMirror is null or cannot be resolved to a declared type, - * the method returns false. - *

- * - *

Example Usage

- *
{@code
-     * TypeMirror enumTypeMirror = element.asType(); // represents an enum type like MyEnum
-     * boolean isEnum = TypeUtils.isEnumType(enumTypeMirror); // returns true
-     *
-     * TypeMirror stringTypeMirror = element.asType(); // represents 'java.lang.String'
-     * boolean isEnum = TypeUtils.isEnumType(stringTypeMirror); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the type is an enum type; false otherwise - */ - static boolean isEnumType(TypeMirror type) { - DeclaredType declaredType = ofDeclaredType(type); - return declaredType != null && ENUM == declaredType.asElement().getKind(); - } - - /** - * Checks whether the given Element represents an enum type. - * - *

- * This method determines if the provided Element corresponds to an enum type - * by checking its kind via the underlying element's {@link Element#getKind()}. - * If the provided Element is null or cannot be resolved to a declared type, - * the method returns false. - *

- * - *

Example Usage

- *
{@code
-     * Element enumElement = ...; // represents an enum type like MyEnum
-     * boolean isEnum = TypeUtils.isEnumType(enumElement); // returns true
-     *
-     * Element stringElement = ...; // represents 'java.lang.String'
-     * boolean isEnum = TypeUtils.isEnumType(stringElement); // returns false
-     * }
- * - * @param element the Element to check, may be null - * @return true if the element represents an enum type; false otherwise - */ - static boolean isEnumType(Element element) { - return element != null && isEnumType(element.asType()); - } - - /** - * Checks whether the given {@link TypeMirror} represents a class type. - * A class type is determined by checking if its corresponding element has the kind of a class. - * - *

Example Usage

- *
{@code
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * boolean isClass = TypeUtils.isClassType(classTypeMirror); // returns true
-     *
-     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
-     * boolean isClass = TypeUtils.isClassType(interfaceTypeMirror); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the type is a class type; false otherwise - */ - static boolean isClassType(TypeMirror type) { - DeclaredType declaredType = ofDeclaredType(type); - return declaredType != null && isClassType(declaredType.asElement()); - } - - /** - * Checks whether the given Element represents a class type. - * A class type is determined by checking if its corresponding element has the kind of a class. - * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * boolean isClass = TypeUtils.isClassType(classElement); // returns true
-     *
-     * Element interfaceElement = ...; // represents an interface like MyInterface
-     * boolean isClass = TypeUtils.isClassType(interfaceElement); // returns false
-     * }
- * - * @param element the Element to check, may be null - * @return true if the element represents a class type; false otherwise - */ - static boolean isClassType(Element element) { - return element != null && CLASS == element.getKind(); - } - - /** - * Checks whether the given {@link TypeMirror} represents a primitive type. - * A primitive type is one of the predefined types in Java such as int, boolean, etc. - * - *

Example Usage

- *
{@code
-     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
-     * boolean isPrimitive = TypeUtils.isPrimitiveType(intTypeMirror); // returns true
-     *
-     * TypeMirror stringTypeMirror = elementUtils.getTypeElement("java.lang.String").asType();
-     * boolean isPrimitiveString = TypeUtils.isPrimitiveType(stringTypeMirror); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the type is a primitive type; false otherwise - */ - static boolean isPrimitiveType(TypeMirror type) { - return type != null && type.getKind().isPrimitive(); - } - - /** - * Checks whether the given Element represents a primitive type. - * A primitive type is one of the predefined types in Java such as int, boolean, etc. - * - *

Example Usage

- *
{@code
-     * Element intElement = ...; // represents 'int'
-     * boolean isPrimitive = TypeUtils.isPrimitiveType(intElement); // returns true
-     *
-     * Element stringElement = ...; // represents 'java.lang.String'
-     * boolean isPrimitiveString = TypeUtils.isPrimitiveType(stringElement); // returns false
-     * }
- * - * @param element the Element to check, may be null - * @return true if the element represents a primitive type; false otherwise - */ - static boolean isPrimitiveType(Element element) { - return element != null && isPrimitiveType(element.asType()); - } - - /** - * Checks whether the given {@link TypeMirror} represents an interface type. - * An interface type is determined by checking if its corresponding element has the kind of an interface. - * - *

Example Usage

- *
{@code
-     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
-     * boolean isInterface = TypeUtils.isInterfaceType(interfaceTypeMirror); // returns true
-     *
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * boolean isInterface = TypeUtils.isInterfaceType(classTypeMirror); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the type is an interface type; false otherwise - */ - static boolean isInterfaceType(TypeMirror type) { - DeclaredType declaredType = ofDeclaredType(type); - return declaredType != null && isInterfaceType(declaredType.asElement()); - } - - /** - * Checks whether the given Element represents an interface type. - * An interface type is determined by checking if its corresponding element has the kind of an interface. - * - *

Example Usage

- *
{@code
-     * Element interfaceElement = ...; // represents an interface like MyInterface
-     * boolean isInterface = TypeUtils.isInterfaceType(interfaceElement); // returns true
-     *
-     * Element classElement = ...; // represents a class like MyClass
-     * boolean isInterface = TypeUtils.isInterfaceType(classElement); // returns false
-     * }
- * - * @param element the Element to check, may be null - * @return true if the element represents an interface type; false otherwise - */ - static boolean isInterfaceType(Element element) { - return element != null && INTERFACE == element.getKind(); - } - - /** - * Checks whether the given {@link TypeMirror} represents an annotation type. - * An annotation type is determined by checking if its corresponding element has the kind of an annotation. - * - *

Example Usage

- *
{@code
-     * TypeMirror annotationTypeMirror = element.asType(); // represents an annotation like MyAnnotation
-     * boolean isAnnotation = TypeUtils.isAnnotationType(annotationTypeMirror); // returns true
-     *
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * boolean isAnnotation = TypeUtils.isAnnotationType(classTypeMirror); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the type is an annotation type; false otherwise - */ - static boolean isAnnotationType(TypeMirror type) { - DeclaredType declaredType = ofDeclaredType(type); - return declaredType != null && isAnnotationType(declaredType.asElement()); - } - - /** - * Checks whether the given Element represents an annotation type. - * An annotation type is determined by checking if its corresponding element has the kind of an annotation. - * - *

Example Usage

- *
{@code
-     * Element annotationElement = ...; // represents an annotation like MyAnnotation
-     * boolean isAnnotation = TypeUtils.isAnnotationType(annotationElement); // returns true
-     *
-     * Element classElement = ...; // represents a class like MyClass
-     * boolean isAnnotation = TypeUtils.isAnnotationType(classElement); // returns false
-     * }
- * - * @param element the Element to check, may be null - * @return true if the element represents an annotation type; false otherwise - */ - static boolean isAnnotationType(Element element) { - return element != null && ANNOTATION_TYPE == element.getKind(); - } - - /** - * Checks if the given Element is a TypeElement. - * - *

- * This method verifies whether the provided Element represents a type element, - * such as a class, interface, enum, or annotation type. If the element is null, - * it returns false. - *

- * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * boolean isType = TypeUtils.isTypeElement(classElement); // returns true
-     *
-     * Element packageElement = ...; // represents a package
-     * boolean isType = TypeUtils.isTypeElement(packageElement); // returns false
-     * }
- * - * @param element The Element to check, may be null. - * @return true if the element is a TypeElement; false otherwise. - */ - static boolean isTypeElement(Element element) { - return element instanceof TypeElement; - } - - /** - * Checks if the given TypeMirror represents a TypeElement. - * A TypeElement is a type that corresponds to a class, interface, enum, or annotation type - * that has been explicitly defined in the source code. - * - *

Example Usage

- *
{@code
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * boolean isType = TypeUtils.isTypeElement(classTypeMirror); // returns true
-     *
-     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
-     * boolean isTypePrimitive = TypeUtils.isTypeElement(intTypeMirror); // returns false
-     *
-     * TypeMirror stringArrayTypeMirror = processingEnv.getTypeUtils().getArrayType(...); // represents String[]
-     * boolean isTypeArray = TypeUtils.isTypeElement(stringArrayTypeMirror); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the TypeMirror represents a TypeElement; false otherwise - */ - static boolean isTypeElement(TypeMirror type) { - DeclaredType declaredType = ofDeclaredType(type); - return declaredType != null && isTypeElement(declaredType.asElement()); - } - - /** - * Checks whether the given Element represents a declared type. - * A declared type is a type that corresponds to a class, interface, enum, or annotation type - * that has been explicitly defined in the source code. - * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * boolean isDeclared = TypeUtils.isDeclaredType(classElement); // returns true
-     *
-     * Element packageElement = ...; // represents a package
-     * boolean isDeclaredPackage = TypeUtils.isDeclaredType(packageElement); // returns false
-     * }
- * - * @param element the Element to check, may be null - * @return true if the element represents a DeclaredType; false otherwise - */ - static boolean isDeclaredType(Element element) { - return element != null && isDeclaredType(element.asType()); - } - - /** - * Checks whether the given {@link TypeMirror} represents a declared type. - * A declared type is a type that corresponds to a class, interface, enum, or annotation type - * that has been explicitly defined in the source code. - * - *

Example Usage

- *
{@code
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * boolean isDeclared = TypeUtils.isDeclaredType(classTypeMirror); // returns true
-     *
-     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
-     * boolean isDeclaredPrimitive = TypeUtils.isDeclaredType(intTypeMirror); // returns false
-     *
-     * TypeMirror stringArrayTypeMirror = processingEnv.getTypeUtils().getArrayType(...); // represents String[]
-     * boolean isDeclaredArray = TypeUtils.isDeclaredType(stringArrayTypeMirror); // returns false
-     * }
- * - * @param type the TypeMirror to check, may be null - * @return true if the type is a declared type; false otherwise - */ - static boolean isDeclaredType(TypeMirror type) { - return type instanceof DeclaredType; - } - - /** - * Converts the given Element to a TypeElement if it is an instance of TypeElement. - * - *

- * This method checks if the provided Element represents a type element such as a class, interface, - * enum, or annotation type. If the element is null or not a TypeElement, this method returns null. - *

- * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * TypeElement typeElement = TypeUtils.ofTypeElement(classElement); // returns a valid TypeElement
-     *
-     * Element packageElement = ...; // represents a package
-     * TypeElement typeElementForPackage = TypeUtils.ofTypeElement(packageElement); // returns null
-     * }
- * - * @param element The Element to convert, may be null. - * @return The converted TypeElement if the element is a TypeElement; otherwise, null. - */ - static TypeElement ofTypeElement(Element element) { - return isTypeElement(element) ? (TypeElement) element : null; - } - - /** - * Converts the given TypeMirror to a TypeElement if it represents a declared type. - * - *

- * This method checks if the provided TypeMirror corresponds to a declared type - * (such as a class, interface, enum, or annotation type). If it does, the corresponding - * TypeElement is returned. If the TypeMirror is null or not a declared type, - * this method returns null. - *

- * - *

Example Usage

- *
{@code
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * TypeElement typeElement = TypeUtils.ofTypeElement(classTypeMirror); // returns a valid TypeElement
-     *
-     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
-     * TypeElement primitiveTypeElement = TypeUtils.ofTypeElement(intTypeMirror); // returns null
-     * }
- * - * @param type The TypeMirror to convert, may be null. - * @return The corresponding TypeElement if the TypeMirror represents a declared type; - * otherwise, null if the type is null or not a DeclaredType. - */ - @Nullable - static TypeElement ofTypeElement(TypeMirror type) { - DeclaredType declaredType = ofDeclaredType(type); - return ofTypeElement(declaredType); - } - - /** - * Converts the given DeclaredType to a TypeElement if it is not null. - * - *

This method attempts to convert the provided {@link DeclaredType} to a corresponding - * {@link TypeElement}. If the declared type is null, or if it cannot be resolved to a type element, - * this method returns null. - * - *

Example Usage

- *
{@code
-     * DeclaredType declaredType = ...; // represents a declared type like MyClass
-     * TypeElement typeElement = TypeUtils.ofTypeElement(declaredType); // returns a valid TypeElement if available
-     *
-     * DeclaredType nullDeclaredType = null;
-     * TypeElement nullTypeElement = TypeUtils.ofTypeElement(nullDeclaredType); // returns null
-     * }
- * - * @param declaredType the DeclaredType to convert, may be null - * @return the corresponding TypeElement if the DeclaredType is not null; - * otherwise, null - */ - @Nullable - static TypeElement ofTypeElement(DeclaredType declaredType) { - if (declaredType != null) { - return ofTypeElement(declaredType.asElement()); - } - return null; - } - - /** - * Converts the given Element to a DeclaredType by first converting it to a TypeElement. - * If the element is null or cannot be converted to a DeclaredType, returns null. - * - *

- * This method checks if the provided Element represents a type element (class, interface, enum, or annotation). - * If it does, the corresponding TypeElement is obtained, and its asType() method is called to retrieve the TypeMirror. - * Then, the TypeMirror is converted to a DeclaredType if it represents a declared type. - *

- * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * DeclaredType declaredType = TypeUtils.ofDeclaredType(classElement); // returns a valid DeclaredType
-     *
-     * Element packageElement = ...; // represents a package
-     * DeclaredType packageDeclaredType = TypeUtils.ofDeclaredType(packageElement); // returns null
-     * }
- * - * @param element The Element to convert, may be null. - * @return The corresponding DeclaredType if the element is valid and represents a declared type; - * otherwise, null if the element is null or conversion fails. - */ - @Nullable - static DeclaredType ofDeclaredType(Element element) { - return element == null ? null : ofDeclaredType(element.asType()); - } - - /** - * Converts the given TypeMirror to a DeclaredType if it represents a declared type. - * A declared type is a type that corresponds to a class, interface, enum, or annotation type - * that has been explicitly defined in the source code. - * - *

Example Usage

- *
{@code
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * DeclaredType declaredType = TypeUtils.ofDeclaredType(classTypeMirror); // returns a valid DeclaredType
-     *
-     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
-     * DeclaredType primitiveDeclaredType = TypeUtils.ofDeclaredType(intTypeMirror); // returns null
-     *
-     * TypeMirror stringArrayTypeMirror = processingEnv.getTypeUtils().getArrayType(...); // represents String[]
-     * DeclaredType arrayDeclaredType = TypeUtils.ofDeclaredType(stringArrayTypeMirror); // returns null
-     * }
- * - * @param type The TypeMirror to convert, may be null. - * @return The corresponding DeclaredType if the TypeMirror represents a declared type; - * otherwise, null if the type is null or not a DeclaredType. - */ - @Nullable - static DeclaredType ofDeclaredType(TypeMirror type) { - return isDeclaredType(type) ? (DeclaredType) type : null; - } - - /** - * Converts an array of Elements to a List of TypeMirrors. - * If the input array is null or empty, returns an empty list. - * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * Element interfaceElement = ...; // represents an interface like MyInterface
-     *
-     * List typeMirrors = TypeUtils.ofTypeMirrors(classElement, interfaceElement);
-     * // typeMirrors now contains the TypeMirror of MyClass and MyInterface
-     *
-     * List emptyList = TypeUtils.ofTypeMirrors(); // returns an empty list
-     * }
- * - * @param elements the array of Elements to convert - * @return a List of TypeMirrors derived from the given Elements - */ - @Nonnull - @Immutable - static List ofTypeMirrors(Element... elements) { - return ofTypeMirrors(ofList(elements)); - } - - /** - * Converts a collection of Elements to a list of TypeMirrors. - * Optionally applies an array of predicates to filter the resulting TypeMirrors. - * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * Element interfaceElement = ...; // represents an interface like MyInterface
-     *
-     * List typeMirrors = TypeUtils.ofTypeMirrors(Arrays.asList(classElement, interfaceElement));
-     * // typeMirrors now contains the TypeMirror of MyClass and MyInterface
-     *
-     * List emptyList = TypeUtils.ofTypeMirrors(Collections.emptyList()); // returns an empty list
-     * }
- * - * @param elements The collection of Elements to convert. Must not be null. - * @return A list of TypeMirrors derived from the given Elements. - */ - @Nonnull - @Immutable - static List ofTypeMirrors(Collection elements) { - return ofTypeMirrors(elements, EMPTY_PREDICATE_ARRAY); - } - - @Nonnull - @Immutable - static List ofTypeMirrors(Collection elements, Predicate... typeFilters) { - return isEmpty(elements) ? emptyList() : - unmodifiableList(elements.stream().map(Element::asType).filter(and(typeFilters)).collect(toList())); - } - - /** - * Converts an array of TypeMirrors to a List of TypeElements. - * If the input array is null or empty, returns an empty list. - * - *

Example Usage

- *
{@code
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
-     *
-     * List typeElements = TypeUtils.ofTypeElements(classTypeMirror, interfaceTypeMirror);
-     * // typeElements now contains the TypeElement of MyClass and MyInterface
-     *
-     * List emptyList = TypeUtils.ofTypeElements(); // returns an empty list
-     * }
- * - * @param types The array of TypeMirrors to convert. May be null or contain null elements. - * @return A List of TypeElements derived from the given TypeMirrors. - */ - @Nonnull - @Immutable - static List ofTypeElements(TypeMirror... types) { - return ofTypeElements(ofList(types)); - } - - /** - * Converts a collection of TypeMirrors to a list of TypeElements. - * Optionally applies an array of predicates to filter the resulting TypeElements. - * - *

Example Usage

- *
{@code
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
-     *
-     * List typeElements = TypeUtils.ofTypeElements(Arrays.asList(classTypeMirror, interfaceTypeMirror));
-     * // typeElements now contains the TypeElement of MyClass and MyInterface
-     *
-     * List emptyList = TypeUtils.ofTypeElements(Collections.emptyList()); // returns an empty list
-     * }
- * - * @param types The collection of TypeMirrors to convert. Must not be null. - * @return A list of TypeElements derived from the given TypeMirrors. - */ - @Nonnull - @Immutable - static List ofTypeElements(Collection types) { - return ofTypeElements(types, EMPTY_PREDICATE_ARRAY); - } - - /** - * Converts a collection of TypeMirrors to a list of TypeElements. - * Optionally applies an array of predicates to filter the resulting TypeElements. - * - *

Example Usage

- *
{@code
-     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
-     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
-     *
-     * List typeElements = TypeUtils.ofTypeElements(Arrays.asList(classTypeMirror, interfaceTypeMirror));
-     * // typeElements now contains the TypeElement of MyClass and MyInterface
-     *
-     * List emptyList = TypeUtils.ofTypeElements(Collections.emptyList()); // returns an empty list
-     * }
- * - * @param types The collection of TypeMirrors to convert. Must not be null. - * @param typeFilters Optional predicates to filter the TypeElements. May be null or empty. - * @return A list of TypeElements derived from the given TypeMirrors, filtered by the provided predicates. - */ - @Nonnull - @Immutable - static List ofTypeElements(Collection types, Predicate... typeFilters) { - return isEmpty(types) ? emptyList() : unmodifiableList( - types.stream() - .map(TypeUtils::ofTypeElement) - .filter(Objects::nonNull) - .filter(and(typeFilters)).collect(toList()) - ); - } - - /** - * Converts an array of Elements to a List of DeclaredTypes. - * If the input array is null or empty, returns an empty list. - * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * Element interfaceElement = ...; // represents an interface like MyInterface
-     *
-     * List declaredTypes = TypeUtils.ofDeclaredTypes(classElement, interfaceElement);
-     * // declaredTypes now contains the DeclaredType of MyClass and MyInterface
-     *
-     * List emptyList = TypeUtils.ofDeclaredTypes(); // returns an empty list
-     * }
- * - * @param elements the array of Elements to convert - * @return a List of DeclaredTypes derived from the given Elements - */ - @Nonnull - @Immutable - static List ofDeclaredTypes(Element... elements) { - return ofDeclaredTypes(ofList(elements)); - } - - /** - * Converts a collection of Elements to a list of DeclaredTypes. - * Optionally applies an array of predicates to filter the resulting DeclaredTypes. - * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * Element interfaceElement = ...; // represents an interface like MyInterface
-     *
-     * List declaredTypes = TypeUtils.ofDeclaredTypes(Arrays.asList(classElement, interfaceElement));
-     * // declaredTypes now contains the DeclaredType of MyClass and MyInterface
-     *
-     * List emptyList = TypeUtils.ofDeclaredTypes(Collections.emptyList()); // returns an empty list
-     * }
- * - * @param elements The collection of Elements to convert. Must not be null. - * @return A list of DeclaredTypes derived from the given Elements. - */ - @Nonnull - @Immutable - static List ofDeclaredTypes(Collection elements) { - return ofDeclaredTypes(elements, EMPTY_PREDICATE_ARRAY); - } - - /** - * Converts a collection of Elements to a list of DeclaredTypes. - * Optionally applies an array of predicates to filter the resulting DeclaredTypes. - * - *

Example Usage

- *
{@code
-     * Element classElement = ...; // represents a class like MyClass
-     * Element interfaceElement = ...; // represents an interface like MyInterface
-     *
-     * List declaredTypes = TypeUtils.ofDeclaredTypes(Arrays.asList(classElement, interfaceElement));
-     * // declaredTypes now contains the DeclaredType of MyClass and MyInterface
-     *
-     * List emptyList = TypeUtils.ofDeclaredTypes(Collections.emptyList()); // returns an empty list
-     * }
- * - * @param elements The collection of Elements to convert. Must not be null. - * @param typeFilters Optional predicates to filter the DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes derived from the given Elements, filtered by the provided predicates. - */ - @Nonnull - @Immutable - static List ofDeclaredTypes(Collection elements, - Predicate... typeFilters) { - - if (isEmpty(elements)) { - return emptyList(); - } - - List declaredTypes = elements.stream() - .map(TypeUtils::ofTypeElement) - .filter(Objects::nonNull) - .map(Element::asType) - .map(TypeUtils::ofDeclaredType) - .filter(Objects::nonNull) - .filter(and(typeFilters)) - .collect(toList()); - - return declaredTypes.isEmpty() ? emptyList() : unmodifiableList(declaredTypes); - } - - /** - * Retrieves the TypeElement representing the superclass of the given TypeElement. - * - *

If the provided TypeElement is null or represents a class without a superclass - * (e.g., {@link Object}, an interface, or an enum), this method returns null. - * - *

Example Usage

- *
{@code
-     * TypeElement type = ...; // represents a class like MyClass which extends SomeBaseClass
-     * TypeElement superClass = TypeUtils.getTypeElementOfSuperclass(type);
-     * // superClass now represents SomeBaseClass if available
-     *
-     * TypeElement interfaceType = ...; // represents an interface
-     * TypeElement superClassForInterface = TypeUtils.getTypeElementOfSuperclass(interfaceType);
-     * // superClassForInterface will be null since interfaces do not have a superclass
-     *
-     * TypeElement objectType = processingEnv.getElementUtils().getTypeElement("java.lang.Object");
-     * TypeElement superClassForObject = TypeUtils.getTypeElementOfSuperclass(objectType);
-     * // superClassForObject will be null since Object has no superclass
-     * }
- * - * @param type the TypeElement whose superclass is to be retrieved, may be null - * @return the TypeElement of the superclass if available; otherwise, null - */ - @Nullable - static TypeElement getTypeElementOfSuperclass(TypeElement type) { - return type == null ? null : ofTypeElement(type.getSuperclass()); - } - - /** - * Retrieves all TypeElements representing the superclasses and interfaces in the hierarchy of the given TypeElement. - * This includes both direct and indirect superclasses as well as implemented interfaces from the entire hierarchy. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List superTypes = TypeUtils.getAllTypeElementsOfSuperTypes(type);
-     * // superTypes now contains all superclasses and interfaces in the hierarchy of MyClass
-     *
-     * TypeElement interfaceType = processingEnv.getElementUtils().getTypeElement("com.example.MyInterface");
-     * List interfaceSuperTypes = TypeUtils.getAllTypeElementsOfSuperTypes(interfaceType);
-     * // interfaceSuperTypes now contains all superinterfaces of MyInterface
-     *
-     * List emptyList = TypeUtils.getAllTypeElementsOfSuperTypes(null); // returns an empty list
-     * }
- * - * @param type The TypeElement whose hierarchy is to be explored, may be null. - * @return A list of TypeElements representing all superclasses and interfaces in the hierarchy of the provided TypeElement. - * Returns an empty list if the input is null or no super types exist. - */ - @Nonnull - @Immutable - static List getAllTypeElementsOfSuperTypes(TypeElement type) { - return findAllTypeElementsOfSuperTypes(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves all TypeElements representing the superclasses in the hierarchy of the given TypeElement. - * This includes direct and indirect superclasses, but excludes interfaces. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List superclasses = TypeUtils.getAllTypeElementsOfSuperclasses(type);
-     * // superclasses now contains all superclass TypeElements in the hierarchy of MyClass
-     *
-     * List emptyList = TypeUtils.getAllTypeElementsOfSuperclasses(null); // returns an empty list
-     * }
- * - * @param type The TypeElement whose superclass hierarchy is to be explored, may be null. - * @return A list of TypeElements representing all superclasses in the hierarchy of the provided TypeElement. - * Returns an empty list if the input is null or no superclasses exist in the hierarchy. - */ - @Nonnull - @Immutable - static List getAllTypeElementsOfSuperclasses(TypeElement type) { - return findAllTypeElementsOfSuperclasses(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves the direct interface types implemented by the given TypeElement. - * This method only returns interfaces directly declared on the specified type, - * and does not include superinterfaces from the entire hierarchy. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List interfaces = TypeUtils.getTypeElementsOfInterfaces(type);
-     * // interfaces now contains the directly implemented interfaces of MyClass
-     *
-     * List emptyList = TypeUtils.getTypeElementsOfInterfaces(null); // returns an empty list
-     * }
- * - * @param type The TypeElement whose directly implemented interfaces are to be retrieved, may be null. - * @return A list of TypeElements representing the directly implemented interfaces. - * Returns an empty list if the input is null or no interfaces are directly implemented. - */ - @Nonnull - @Immutable - static List getTypeElementsOfInterfaces(TypeElement type) { - return findTypeElementsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves all TypeElements representing the interfaces implemented in the entire hierarchy of the given TypeElement. - * This includes both directly and indirectly implemented interfaces from superclasses and superinterfaces. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List interfaces = TypeUtils.getAllTypeElementsOfInterfaces(type);
-     * // interfaces now contains all interfaces implemented by MyClass, including those from superclasses
-     *
-     * List emptyList = TypeUtils.getAllTypeElementsOfInterfaces(null); // returns an empty list
-     * }
- * - * @param type The TypeElement whose interface hierarchy is to be explored, may be null. - * @return A list of TypeElements representing all implemented interfaces in the hierarchy of the provided TypeElement. - * Returns an empty list if the input is null or no interfaces are found in the hierarchy. - */ - @Nonnull - @Immutable - static List getAllTypeElementsOfInterfaces(TypeElement type) { - return findAllTypeElementsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves the directly associated TypeElements of the given TypeElement. - * This includes: - * - The type itself - * - Direct superclass (if any) - * - Directly implemented interfaces (if any) - * - *

This method does not traverse the entire hierarchy. It only includes direct relationships. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List directTypes = TypeUtils.getTypeElements(type);
-     * // directTypes contains:
-     * // - MyClass itself
-     * // - Its direct superclass (if any)
-     * // - Interfaces directly implemented by MyClass (if any)
-     *
-     * List emptyList = TypeUtils.getTypeElements(null); // returns an empty list
-     * }
- * - * @param type The TypeElement to retrieve directly associated types from, may be null. - * @return A list of TypeElements representing the directly associated types. - * Returns an empty list if the input is null or no direct associations exist. - */ - @Nonnull - @Immutable - static List getTypeElements(TypeElement type) { - return getTypeElements(type, true, false, true, true); - } - - /** - * Retrieves all TypeElements associated with the given TypeElement, including: - * - The type itself - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List allTypes = TypeUtils.getAllTypeElements(type);
-     * // allTypes contains:
-     * // - MyClass itself
-     * // - All superclasses in the hierarchy (e.g., Object, SomeBaseClass)
-     * // - All implemented interfaces, including those from superclasses and superinterfaces
-     *
-     * List emptyList = TypeUtils.getAllTypeElements(null); // returns an empty list
-     * }
- * - * @param type The TypeElement to retrieve all associated types from, may be null. - * @return A list of TypeElements representing all associated types in the hierarchy. - * Returns an empty list if the input is null or no types are found. - */ - @Nonnull - @Immutable - static List getAllTypeElements(TypeElement type) { - return getTypeElements(type, true, true, true, true); - } - - /** - * Retrieves a list of TypeElements associated with the given TypeElement based on the specified inclusion criteria. - * - *

- * This method allows fine-grained control over which types are included in the result: - *

- * - *
    - *
  • {@code includeSelf} - Whether to include the given TypeElement itself in the result.
  • - *
  • {@code includeHierarchicalTypes} - Whether to include types from the entire hierarchy (e.g., superclasses and interfaces).
  • - *
  • {@code includeSuperclass} - Whether to include direct or hierarchical superclasses based on the value of includeHierarchicalTypes.
  • - *
  • {@code includeSuperInterfaces} - Whether to include direct or hierarchical interfaces based on the value of includeHierarchicalTypes.
  • - *
- * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     *
-     * // Get the type itself and all direct superclasses and interfaces
-     * List directTypes = TypeUtils.getTypeElements(type, false, false, true, true);
-     *
-     * // Get the type itself and all hierarchical superclasses and interfaces
-     * List hierarchicalTypes = TypeUtils.getTypeElements(type, true, true, true, true);
-     *
-     * // Get only the direct superclasses without including interfaces
-     * List superclassesOnly = TypeUtils.getTypeElements(type, false, false, true, false);
-     *
-     * // Get only the hierarchical interfaces
-     * List interfacesOnly = TypeUtils.getTypeElements(type, false, true, false, true);
-     * }
- * - * @param type The TypeElement to find associated types for, may be null. - * @param includeSelf Whether to include the type itself in the result. - * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). - * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. - * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. - * @return A list of TypeElements representing the associated types according to the inclusion criteria. - * Returns an empty list if the input type is null or no matching types are found. - */ - @Nonnull - @Immutable - static List getTypeElements(TypeElement type, - boolean includeSelf, - boolean includeHierarchicalTypes, - boolean includeSuperClasses, - boolean includeSuperInterfaces) { - return findTypeElements(type, includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves the TypeElements representing the interfaces directly implemented by the given TypeElement. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include superinterfaces from the entire hierarchy. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List interfaces = TypeUtils.findTypeElementsOfInterfaces(type);
-     * // interfaces now contains the directly implemented interfaces of MyClass
-     *
-     * List emptyList = TypeUtils.findTypeElementsOfInterfaces(null); // returns an empty list
-     * }
- * - * @param type The TypeElement whose directly implemented interfaces are to be retrieved, may be null. - * @param interfaceFilters Optional predicates to filter the resulting TypeElements. May be null or empty. - * @return A list of TypeElements representing the directly implemented interfaces. - * Returns an empty list if the input is null or no interfaces are directly implemented. - */ - @Nonnull - @Immutable - static List findTypeElementsOfInterfaces(TypeElement type, Predicate... interfaceFilters) { - return findTypeElements(type, false, false, false, true, interfaceFilters); - } - - /** - * Retrieves all TypeElements representing the superclasses in the hierarchy of the given TypeElement. - * This includes direct and indirect superclasses, but excludes interfaces. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List superclasses = TypeUtils.findAllTypeElementsOfSuperclasses(type);
-     * // superclasses now contains all superclass TypeElements in the hierarchy of MyClass
-     *
-     * List emptyList = TypeUtils.findAllTypeElementsOfSuperclasses(null); // returns an empty list
-     * }
- * - * @param type The TypeElement whose superclass hierarchy is to be explored, may be null. - * @param typeFilters Optional predicates to filter the resulting TypeElements. May be null or empty. - * @return A list of TypeElements representing all superclasses in the hierarchy of the provided TypeElement. - * Returns an empty list if the input is null or no superclasses exist in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllTypeElementsOfSuperclasses(TypeElement type, Predicate... typeFilters) { - return findTypeElements(type, false, true, true, false, typeFilters); - } - - /** - * Retrieves all TypeElements representing the interfaces implemented in the entire hierarchy of the given TypeElement. - * This includes both directly and indirectly implemented interfaces from superclasses and superinterfaces. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List interfaces = TypeUtils.findAllTypeElementsOfInterfaces(type);
-     * // interfaces now contains all interfaces implemented by MyClass, including those from superclasses
-     *
-     * List emptyList = TypeUtils.findAllTypeElementsOfInterfaces(null); // returns an empty list
-     * }
- * - * @param type The TypeElement whose interface hierarchy is to be explored, may be null. - * @return A list of TypeElements representing all implemented interfaces in the hierarchy of the provided TypeElement. - * Returns an empty list if the input is null or no interfaces are found in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllTypeElementsOfInterfaces(TypeElement type, Predicate... interfaceFilters) { - return findTypeElements(type, false, true, false, true, interfaceFilters); - } - - /** - * Retrieves all TypeElements representing the superclasses and interfaces in the hierarchy of the given TypeElement. - * This includes both direct and indirect superclasses as well as implemented interfaces from the entire hierarchy. - * Optionally applies an array of predicates to filter the resulting TypeElements. - * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List superTypes = TypeUtils.findAllTypeElementsOfSuperTypes(type);
-     * // superTypes now contains all superclasses and interfaces in the hierarchy of MyClass
-     *
-     * List filteredSuperTypes = TypeUtils.findAllTypeElementsOfSuperTypes(type,
-     *     t -> t.getQualifiedName().toString().startsWith("com.example"));
-     * // filteredSuperTypes contains only those superTypes whose qualified names start with "com.example"
-     *
-     * List emptyList = TypeUtils.findAllTypeElementsOfSuperTypes(null); // returns an empty list
-     * }
- * - * @param type The TypeElement whose hierarchy is to be explored, may be null. - * @param typeFilters Optional predicates to filter the resulting TypeElements. May be null or empty. - * @return A list of TypeElements representing all superclasses and interfaces in the hierarchy of the provided TypeElement, - * filtered by the provided predicates. Returns an empty list if the input is null or no types are found in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllTypeElementsOfSuperTypes(TypeElement type, Predicate... typeFilters) { - return findTypeElements(type, false, true, true, true, typeFilters); - } - - /** - * Finds and returns a list of TypeElements based on the specified criteria. - * - *

This method allows detailed control over which types are included in the result: - * - Whether to include the type itself - * - Whether to include hierarchical types (e.g., superclasses and interfaces) - * - Whether to include superclasses and/or interfaces based on the inclusion criteria

- * - *

Example Usage

- *
{@code
-     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     *
-     * // Get the type itself and all direct superclasses and interfaces
-     * List directTypes = TypeUtils.findTypeElements(type, true, false, true, true);
-     *
-     * // Get all types in the hierarchy including superclasses and interfaces
-     * List hierarchicalTypes = TypeUtils.findTypeElements(type, true, true, true, true);
-     *
-     * // Get only direct superclasses without including interfaces
-     * List superclassesOnly = TypeUtils.findTypeElements(type, false, false, true, false);
-     *
-     * // Get only interfaces from the entire hierarchy
-     * List interfacesOnly = TypeUtils.findTypeElements(type, false, true, false, true);
-     * }
- * - * @param type The TypeElement to start the search from, may be null. - * @param includeSelf Whether to include the type itself in the result. - * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). - * @param includeSuperclass Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. - * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. - * @param typeFilters Optional predicates to filter the resulting TypeElements. May be null or empty. - * @return A list of TypeElements matching the specified criteria. - * Returns an empty list if the input type is null or no matching types are found. - * @throws IllegalArgumentException if any element of 'typeFilters' array is null. - */ - @Nonnull - @Immutable - static List findTypeElements(TypeElement type, - boolean includeSelf, - boolean includeHierarchicalTypes, - boolean includeSuperclass, - boolean includeSuperInterfaces, - Predicate... typeFilters) throws IllegalArgumentException { - if (type == null) { - return emptyList(); - } - assertNoNullElements(typeFilters, () -> "Any element of 'typeFilters' array must not be null"); - return typeElementFinder(type, includeSelf, includeHierarchicalTypes, includeSuperclass, includeSuperInterfaces).findTypes(typeFilters); - } - - /** - * Retrieves the declared type of the superclass for the given Element. - * If the provided Element is null or does not represent a type with a superclass, - * this method will return null. - * - *

Example Usage

- *
{@code
-     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * DeclaredType superClassType = TypeUtils.getDeclaredTypeOfSuperclass(typeElement);
-     * // superClassType now contains the DeclaredType of the superclass of MyClass, if available
-     *
-     * Element interfaceElement = processingEnv.getElementUtils().getTypeElement("com.example.MyInterface");
-     * DeclaredType superClassTypeForInterface = TypeUtils.getDeclaredTypeOfSuperclass(interfaceElement);
-     * // superClassTypeForInterface will be null since interfaces do not have a superclass
-     *
-     * DeclaredType nullCase = TypeUtils.getDeclaredTypeOfSuperclass(null);
-     * // nullCase will be null since the input is null
-     * }
- * - * @param typeElement the Element to retrieve the superclass declared type from, may be null - * @return the DeclaredType representing the superclass of the given Element, or null if none exists - */ - @Nullable - static DeclaredType getDeclaredTypeOfSuperclass(Element typeElement) { - return typeElement == null ? null : getDeclaredTypeOfSuperclass(typeElement.asType()); - } - - /** - * Retrieves the declared type of the superclass for the given TypeMirror. - * If the provided TypeMirror is null or does not represent a type with a superclass, - * this method will return null. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
-     * DeclaredType superClassType = TypeUtils.getDeclaredTypeOfSuperclass(type);
-     * // superClassType contains the DeclaredType of the superclass if available
-     *
-     * DeclaredType nullCase = TypeUtils.getDeclaredTypeOfSuperclass(null);
-     * // nullCase will be null since the input is null
-     * }
- * - * @param type the TypeMirror to retrieve the superclass declared type from, may be null - * @return the DeclaredType representing the superclass of the given TypeMirror, or null if none exists - */ - @Nullable - static DeclaredType getDeclaredTypeOfSuperclass(TypeMirror type) { - TypeElement superType = getTypeElementOfSuperclass(ofTypeElement(type)); - return superType == null ? null : ofDeclaredType(superType.asType()); - } - - /** - * Retrieves a list of declared types representing the interfaces directly implemented by the given Element. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include interfaces from superclasses or superinterfaces. - * - *

Example Usage

- *
{@code
-     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List interfaces = TypeUtils.getDeclaredTypesOfInterfaces(typeElement);
-     * // interfaces now contains the directly implemented interfaces of MyClass
-     *
-     * List emptyList = TypeUtils.getDeclaredTypesOfInterfaces(null); // returns an empty list
-     * }
- * - * @param element The Element whose directly implemented interfaces are to be retrieved, may be null. - * @return A list of DeclaredTypes representing the interfaces directly implemented by the given Element. - * Returns an empty list if the input is null or no interfaces are directly implemented. - */ - @Nonnull - @Immutable - static List getDeclaredTypesOfInterfaces(Element element) { - return element == null ? emptyList() : findDeclaredTypesOfInterfaces(element.asType(), EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of declared types representing the interfaces directly implemented by the given TypeMirror. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include interfaces from superclasses or superinterfaces. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
-     * List interfaces = TypeUtils.getDeclaredTypesOfInterfaces(type);
-     * // interfaces now contains the directly implemented interfaces of the given type
-     *
-     * List emptyList = TypeUtils.getDeclaredTypesOfInterfaces(null); // returns an empty list
-     * }
- * - * @param type The TypeMirror whose directly implemented interfaces are to be retrieved, may be null. - * @return A list of DeclaredTypes representing the interfaces directly implemented by the given TypeMirror. - * Returns an empty list if the input is null or no interfaces are directly implemented. - */ - @Nonnull - @Immutable - static List getDeclaredTypesOfInterfaces(TypeMirror type) { - return findDeclaredTypesOfInterfaces(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes representing all superclasses in the hierarchy of the given Element. - * This includes both direct and indirect superclasses, traversing up through the entire type hierarchy. - * If the provided Element is null or does not have any superclasses, an empty list is returned. - * - *

Example Usage

- *
{@code
-     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List superclasses = TypeUtils.getAllDeclaredTypesOfSuperclasses(typeElement);
-     * // superclasses now contains all superclass DeclaredTypes in the hierarchy of MyClass
-     *
-     * List emptyList = TypeUtils.getAllDeclaredTypesOfSuperclasses(null); // returns an empty list
-     * }
- * - * @param type The Element to retrieve superclass declared types from, may be null. - * @return A list of DeclaredTypes representing all superclasses in the hierarchy of the provided Element. - * Returns an empty list if the input is null or no superclasses exist in the hierarchy. - */ - @Nonnull - @Immutable - static List getAllDeclaredTypesOfSuperclasses(Element type) { - return type == null ? emptyList() : findAllDeclaredTypesOfSuperclasses(type.asType(), EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes representing all superclasses in the hierarchy of the given TypeMirror. - * This includes both direct and indirect superclasses, traversing up through the entire type hierarchy. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
-     * List superclasses = TypeUtils.getAllDeclaredTypesOfSuperclasses(type);
-     * // superclasses now contains all superclass DeclaredTypes in the hierarchy of the given type
-     *
-     * List emptyList = TypeUtils.getAllDeclaredTypesOfSuperclasses(null); // returns an empty list
-     * }
- * - * @param type The TypeMirror whose superclass hierarchy is to be explored, may be null. - * @return A list of DeclaredTypes representing all superclasses in the hierarchy of the provided TypeMirror. - * Returns an empty list if the input is null or no superclasses exist in the hierarchy. - */ - @Nonnull - @Immutable - static List getAllDeclaredTypesOfSuperclasses(TypeMirror type) { - return findAllDeclaredTypesOfSuperclasses(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes representing all interfaces implemented in the entire hierarchy - * of the given Element. This includes both directly and indirectly implemented interfaces from - * superclasses and superinterfaces. - * - *

Example Usage

- *
{@code
-     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List interfaces = TypeUtils.getAllDeclaredTypesOfInterfaces(typeElement);
-     * // interfaces now contains all interfaces implemented by MyClass, including those from superclasses
-     *
-     * List emptyList = TypeUtils.getAllDeclaredTypesOfInterfaces(null); // returns an empty list
-     * }
- * - * @param type The Element whose interface hierarchy is to be explored, may be null. - * @return A list of DeclaredTypes representing all implemented interfaces in the hierarchy of - * the provided Element. Returns an empty list if the input is null or no interfaces - * are found in the hierarchy. - */ - @Nonnull - @Immutable - static List getAllDeclaredTypesOfInterfaces(Element type) { - return type == null ? emptyList() : findAllDeclaredTypesOfInterfaces(type.asType(), EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes representing all interfaces implemented in the entire hierarchy - * of the given TypeMirror. This includes both directly and indirectly implemented interfaces from - * superclasses and superinterfaces. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
-     * List interfaces = TypeUtils.getAllDeclaredTypesOfInterfaces(type);
-     * // interfaces now contains all interfaces implemented by the given type, including those from superclasses
-     *
-     * List emptyList = TypeUtils.getAllDeclaredTypesOfInterfaces(null); // returns an empty list
-     * }
- * - * @param type The TypeMirror whose interface hierarchy is to be explored, may be null. - * @return A list of DeclaredTypes representing all implemented interfaces in the hierarchy of - * the provided TypeMirror. Returns an empty list if the input is null or no interfaces - * are found in the hierarchy. - */ - @Nonnull - @Immutable - static List getAllDeclaredTypesOfInterfaces(TypeMirror type) { - return findAllDeclaredTypesOfInterfaces(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes representing all superclasses and interfaces in the hierarchy - * of the given Element. This includes both direct and indirect superclasses as well as implemented - * interfaces from the entire hierarchy. - * - *

Example Usage

- *
{@code
-     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List superTypes = TypeUtils.getAllDeclaredTypesOfSuperTypes(typeElement);
-     * // superTypes now contains all superclasses and interfaces in the hierarchy of MyClass
-     *
-     * List emptyList = TypeUtils.getAllDeclaredTypesOfSuperTypes(null); // returns an empty list
-     * }
- * - * @param type The Element to retrieve super types from, may be null. - * @return A list of DeclaredTypes representing all superclasses and interfaces in the hierarchy - * of the provided Element. Returns an empty list if the input is null or no types are found. - */ - @Nonnull - @Immutable - static List getAllDeclaredTypesOfSuperTypes(Element type) { - return type == null ? emptyList() : findAllDeclaredTypesOfSuperTypes(type.asType(), EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes representing all superclasses and interfaces in the hierarchy - * of the given TypeMirror. This includes both direct and indirect superclasses as well as implemented - * interfaces from the entire hierarchy. - * - *

Example Usage

- *
{@code
-     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
-     * List superTypes = TypeUtils.getAllDeclaredTypesOfSuperTypes(type);
-     * // superTypes now contains all superclasses and interfaces in the hierarchy of the given type
-     *
-     * List emptyList = TypeUtils.getAllDeclaredTypesOfSuperTypes(null); // returns an empty list
-     * }
- * - * @param type The TypeMirror whose hierarchy is to be explored, may be null. - * @return A list of DeclaredTypes representing all superclasses and interfaces in the hierarchy of - * the provided TypeMirror. Returns an empty list if the input is null or no types are found. - */ - @Nonnull - @Immutable - static List getAllDeclaredTypesOfSuperTypes(TypeMirror type) { - return findAllDeclaredTypesOfSuperTypes(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes representing all associated types of the given Element. - * This includes: - * - The type itself (if it is a declared type) - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces implemented by the type - * - *

Example Usage

- *
{@code
-     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
-     * List allTypes = TypeUtils.getAllDeclaredTypes(typeElement);
-     * // allTypes now contains MyClass itself, its superclasses, and all implemented interfaces
-     *
-     * List emptyList = TypeUtils.getAllDeclaredTypes(null); // returns an empty list
-     * }
- * - * @param type The Element to retrieve associated DeclaredTypes from, may be null. - * @return A list of DeclaredTypes derived from the given Element. Returns an empty list if - * the input is null or no DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List getAllDeclaredTypes(Element type) { - return type == null ? emptyList() : findAllDeclaredTypes(type.asType(), EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy of the given TypeMirror. - * This includes: - * - The type itself (if it is a declared type) - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces implemented by the type - * - * @param type The TypeMirror to retrieve associated DeclaredTypes from, may be null. - * @return A list of DeclaredTypes representing all associated types in the hierarchy of the provided TypeMirror. - * Returns an empty list if the input is null or no DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List getAllDeclaredTypes(TypeMirror type) { - return findAllDeclaredTypes(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of DeclaredTypes associated with the given Element based on the specified inclusion criteria. - * A DeclaredType typically refers to a type that is explicitly declared in the code, such as classes, - * interfaces, enums, or annotation types. - * - * @param type The Element to retrieve DeclaredTypes from, may be null. - * @param includeSelf Whether to include the type itself in the result. - * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). - * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. - * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. - * @return A list of DeclaredTypes derived from the given Element according to the inclusion criteria. - * Returns an empty list if the input is null or no DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List getDeclaredTypes(Element type, - boolean includeSelf, - boolean includeHierarchicalTypes, - boolean includeSuperClasses, - boolean includeSuperInterfaces) { - return getDeclaredTypes(type.asType(), includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces); - } - - /** - * Retrieves a list of DeclaredTypes associated with the given TypeMirror based on the specified inclusion criteria. - * A DeclaredType typically refers to a type that is explicitly declared in the code, such as classes, - * interfaces, enums, or annotation types. - * - * @param type The TypeMirror to retrieve DeclaredTypes from, may be null. - * @param includeSelf Whether to include the type itself in the result. - * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). - * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. - * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. - * @return A list of DeclaredTypes derived from the given TypeMirror according to the inclusion criteria. - * Returns an empty list if the input is null or no DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List getDeclaredTypes(TypeMirror type, - boolean includeSelf, - boolean includeHierarchicalTypes, - boolean includeSuperClasses, - boolean includeSuperInterfaces) { - return findDeclaredTypes(type, includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces, EMPTY_PREDICATE_ARRAY); - } - - - /** - * Finds and returns a list of DeclaredTypes associated with the given TypeMirror, - * excluding any types that match the specified excludedTypes. - * - * @param type The TypeMirror to find associated DeclaredTypes from, may be null. - * @param excludedTypes The array of Types to exclude from the result. May be null or empty. - * @return A list of DeclaredTypes derived from the given TypeMirror, excluding the specified types. - * Returns an empty list if the input type is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findDeclaredTypes(TypeMirror type, Type... excludedTypes) { - return type == null ? emptyList() : findDeclaredTypes(ofTypeElement(type), excludedTypes); - } - - /** - * Finds and returns a list of DeclaredTypes associated with the given Element, - * excluding any types that match the specified excludedTypes. - * - * @param type The Element to find associated DeclaredTypes from, may be null. - * @param excludedTypes The array of Types to exclude from the result. May be null or empty. - * @return A list of DeclaredTypes derived from the given Element, excluding the specified types. - * Returns an empty list if the input type is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findDeclaredTypes(Element type, Type... excludedTypes) { - return type == null ? emptyList() : findDeclaredTypes(type, getTypeNames(excludedTypes)); - } - - /** - * Finds and returns a list of DeclaredTypes associated with the given TypeMirror, - * excluding any types whose names match the specified excludedTypeNames. - * - * @param type The TypeMirror to find associated DeclaredTypes from, may be null. - * @param excludedTypeNames An array of fully qualified type names to exclude from the result. - * May be null or empty. - * @return A list of DeclaredTypes derived from the given TypeMirror, excluding the specified types. - * Returns an empty list if the input type is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findDeclaredTypes(TypeMirror type, CharSequence... excludedTypeNames) { - return type == null ? emptyList() : findDeclaredTypes(ofTypeElement(type), excludedTypeNames); - } - - /** - * Finds and returns a list of DeclaredTypes associated with the given Element, - * excluding any types whose names match the specified excludedTypeNames. - * - * @param type The Element to find associated DeclaredTypes from, may be null. - * @param excludedTypeNames An array of fully qualified type names to exclude from the result. - * May be null or empty. - * @return A list of DeclaredTypes derived from the given Element, excluding the specified types. - * Returns an empty list if the input is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findDeclaredTypes(Element type, CharSequence... excludedTypeNames) { - return type == null ? emptyList() : findDeclaredTypes(type, false, false, true, true, t -> !contains(excludedTypeNames, t.toString())); - } - - /** - * Retrieves a list of DeclaredTypes representing the interfaces directly implemented by the given Element. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include interfaces from superclasses or superinterfaces. - * - * @param type The Element whose directly implemented interfaces are to be retrieved, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes representing the interfaces directly implemented by the given Element. - * Returns an empty list if the input is null or no interfaces are directly implemented. - */ - @Nonnull - @Immutable - static List findDeclaredTypesOfInterfaces(Element type, Predicate... typeFilters) { - return type == null ? emptyList() : findDeclaredTypesOfInterfaces(type.asType(), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing the interfaces directly implemented by the given TypeMirror. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include interfaces from superclasses or superinterfaces. - * - * @param type The TypeMirror whose directly implemented interfaces are to be retrieved, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes representing the interfaces directly implemented by the given TypeMirror. - * Returns an empty list if the input is null or no interfaces are directly implemented. - */ - @Nonnull - @Immutable - static List findDeclaredTypesOfInterfaces(TypeMirror type, Predicate... typeFilters) { - return type == null ? emptyList() : ofDeclaredTypes(getTypeElementsOfInterfaces(ofTypeElement(type)), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing all superclasses in the hierarchy of the given Element. - * This includes both direct and indirect superclasses, traversing up through the entire type hierarchy. - * Optionally applies an array of predicates to filter the resulting DeclaredTypes. - * - * @param type The Element to retrieve superclass declared types from, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes representing all superclasses in the hierarchy of the provided Element. - * Returns an empty list if the input is null or no superclasses exist in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypesOfSuperclasses(Element type, Predicate... typeFilters) { - return type == null ? emptyList() : findAllDeclaredTypesOfSuperclasses(type.asType(), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing all superclasses in the hierarchy of the given TypeMirror. - * This includes both direct and indirect superclasses, traversing up through the entire type hierarchy. - * Optionally applies an array of predicates to filter the resulting DeclaredTypes. - * - * @param type The TypeMirror whose superclass hierarchy is to be explored, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes representing all superclasses in the hierarchy of the provided TypeMirror, - * filtered by the provided predicates. Returns an empty list if the input is null or no superclasses - * exist in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypesOfSuperclasses(TypeMirror type, Predicate... typeFilters) { - return type == null ? emptyList() : ofDeclaredTypes(getAllTypeElementsOfSuperclasses(ofTypeElement(type)), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing all interfaces implemented in the entire hierarchy - * of the given Element. This includes both directly and indirectly implemented interfaces from - * superclasses and superinterfaces. Optionally applies an array of predicates to filter the resulting DeclaredTypes. - * - * @param type The Element whose interface hierarchy is to be explored, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes representing all implemented interfaces in the hierarchy of - * the provided Element, filtered by the provided predicates. Returns an empty list if - * the input is null or no interfaces are found in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypesOfInterfaces(Element type, Predicate... typeFilters) { - return type == null ? emptyList() : findAllDeclaredTypesOfInterfaces(type.asType(), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing all interfaces implemented in the entire hierarchy - * of the given TypeMirror. This includes both directly and indirectly implemented interfaces from - * superclasses and superinterfaces. Optionally applies an array of predicates to filter the resulting DeclaredTypes. - * - * @param type The TypeMirror whose interface hierarchy is to be explored, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes representing all implemented interfaces in the hierarchy of - * the provided TypeMirror, filtered by the provided predicates. Returns an empty list if - * the input is null or no interfaces are found in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypesOfInterfaces(TypeMirror type, Predicate... typeFilters) { - return type == null ? emptyList() : ofDeclaredTypes(getAllTypeElementsOfInterfaces(ofTypeElement(type)), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing all superclasses and interfaces in the hierarchy - * of the given Element. This includes both direct and indirect superclasses as well as implemented - * interfaces from the entire hierarchy. Optionally applies an array of predicates to filter the resulting DeclaredTypes. - * - * @param type The Element to retrieve superclass and interface declared types from, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes representing all superclasses and interfaces in the hierarchy of - * the provided Element, filtered by the provided predicates. Returns an empty list if the - * input is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypesOfSuperTypes(Element type, Predicate... typeFilters) { - return type == null ? emptyList() : findAllDeclaredTypesOfSuperTypes(type.asType(), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing all superclasses and interfaces in the hierarchy - * of the given TypeMirror. This includes both direct and indirect superclasses as well as implemented - * interfaces from the entire hierarchy. Optionally applies an array of predicates to filter the resulting DeclaredTypes. - * - * @param type The TypeMirror whose superclass and interface hierarchy is to be explored, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes representing all superclasses and interfaces in the hierarchy of - * the provided TypeMirror, filtered by the provided predicates. Returns an empty list if the - * input is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypesOfSuperTypes(TypeMirror type, Predicate... typeFilters) { - return type == null ? emptyList() : ofDeclaredTypes(getAllTypeElementsOfSuperTypes(ofTypeElement(type)), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy - * of the given TypeMirror, excluding any types that match the specified excludedTypes. - * This includes: - * - The type itself (if it is a declared type) - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces implemented by the type - * - * @param type The TypeMirror to retrieve associated DeclaredTypes from, may be null. - * @param excludedTypes The array of Types to exclude from the result. May be null or empty. - * @return A list of DeclaredTypes derived from the given TypeMirror, excluding the specified types. - * Returns an empty list if the input is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypes(TypeMirror type, Type... excludedTypes) { - return type == null ? emptyList() : findAllDeclaredTypes(ofTypeElement(type), excludedTypes); - } - - /** - * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy - * of the given Element, excluding any types that match the specified excludedTypes. - * This includes: - * - The type itself (if it is a declared type) - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces implemented by the type - * - * @param type The Element to retrieve associated DeclaredTypes from, may be null. - * @param excludedTypes The array of Types to exclude from the result. May be null or empty. - * @return A list of DeclaredTypes derived from the given Element, excluding the specified types. - * Returns an empty list if the input is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypes(Element type, Type... excludedTypes) { - return type == null ? emptyList() : findAllDeclaredTypes(type, getTypeNames(excludedTypes)); - } - - /** - * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy - * of the given TypeMirror, excluding any types whose names match the specified excludedTypeNames. - * This includes: - * - The type itself (if it is a declared type) - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces implemented by the type - * - * @param type The TypeMirror to retrieve associated DeclaredTypes from, may be null. - * @param excludedTypeNames An array of fully qualified type names to exclude from the result. - * May be null or empty. - * @return A list of DeclaredTypes derived from the given TypeMirror, excluding the specified types. - * Returns an empty list if the input type is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypes(TypeMirror type, CharSequence... excludedTypeNames) { - return type == null ? emptyList() : findAllDeclaredTypes(ofTypeElement(type), excludedTypeNames); - } - - /** - * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy - * of the given Element, excluding any types whose names match the specified excludedTypeNames. - * This includes: - * - The type itself (if it is a declared type) - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces implemented by the type - * - * @param type The Element to retrieve associated DeclaredTypes from, may be null. - * @param excludedTypeNames An array of fully qualified type names to exclude from the result. - * May be null or empty. - * @return A list of DeclaredTypes derived from the given Element, excluding the specified types. - * Returns an empty list if the input is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypes(Element type, CharSequence... excludedTypeNames) { - return type == null ? emptyList() : findAllDeclaredTypes(type, t -> !contains(excludedTypeNames, t.toString())); - } - - /** - * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy - * of the given Element, filtered by the provided predicates. - * This includes: - * - The type itself (if it is a declared type) - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces implemented by the type - * - * @param type The Element to retrieve associated DeclaredTypes from, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes derived from the given Element, filtered by the provided predicates. - * Returns an empty list if the input is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypes(Element type, Predicate... typeFilters) { - return type == null ? emptyList() : findAllDeclaredTypes(type.asType(), typeFilters); - } - - /** - * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy - * of the given TypeMirror, filtered by the provided predicates. - * This includes: - * - The type itself (if it is a declared type) - * - Direct and hierarchical superclasses - * - Direct and hierarchical interfaces implemented by the type - * - * @param type The TypeMirror to retrieve associated DeclaredTypes from, may be null. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes derived from the given TypeMirror, filtered by the provided predicates. - * Returns an empty list if the input is null or no matching DeclaredTypes are found. - */ - @Nonnull - @Immutable - static List findAllDeclaredTypes(TypeMirror type, Predicate... typeFilters) { - return type == null ? emptyList() : ofDeclaredTypes(getAllTypeElements(ofTypeElement(type)), typeFilters); - } - - /** - * Finds and returns a list of DeclaredTypes associated with the given Element based on the specified criteria. - * - *

A DeclaredType typically refers to a type that is explicitly declared in the code, such as classes, - * interfaces, enums, or annotation types. This method allows filtering based on whether to include: - * - The type itself - * - Direct or hierarchical superclasses (based on the includeHierarchicalTypes flag) - * - Direct or hierarchical interfaces (based on the includeHierarchicalTypes flag) - * - * @param type The Element to find associated DeclaredTypes from. May be null. - * @param includeSelf Whether to include the type itself in the result. - * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). - * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. - * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes matching the specified criteria. - * Returns an empty list if the input type is null or no matching types are found. - * @throws IllegalArgumentException if any element of 'typeFilters' array is null. - */ - @Nonnull - @Immutable - static List findDeclaredTypes(Element type, - boolean includeSelf, - boolean includeHierarchicalTypes, - boolean includeSuperClasses, - boolean includeSuperInterfaces, - Predicate... typeFilters) { - return type == null ? emptyList() : findDeclaredTypes(type.asType(), includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces, typeFilters); - } - - /** - * Finds and returns a list of DeclaredTypes associated with the given TypeMirror based on the specified criteria. - * - *

A DeclaredType typically refers to a type that is explicitly declared in the code, such as classes, - * interfaces, enums, or annotation types. This method allows filtering based on whether to include: - * - The type itself - * - Direct or hierarchical superclasses (based on the includeHierarchicalTypes flag) - * - Direct or hierarchical interfaces (based on the includeHierarchicalTypes flag) - * - * @param type The TypeMirror to find associated DeclaredTypes from. May be null. - * @param includeSelf Whether to include the type itself in the result. - * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). - * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. - * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. - * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. - * @return A list of DeclaredTypes matching the specified criteria. - * Returns an empty list if the input type is null or no matching types are found. - * @throws IllegalArgumentException if any element of 'typeFilters' array is null. - */ - @Nonnull - @Immutable - static List findDeclaredTypes(TypeMirror type, - boolean includeSelf, - boolean includeHierarchicalTypes, - boolean includeSuperClasses, - boolean includeSuperInterfaces, - Predicate... typeFilters) { - return type == null ? emptyList() : ofDeclaredTypes(getTypeElements(ofTypeElement(type), includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces), typeFilters); - } - - /** - * Retrieves a list of TypeMirrors representing the interfaces directly implemented by the given TypeMirror. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include interfaces from superclasses or superinterfaces. - * - * @param type The TypeMirror whose directly implemented interfaces are to be retrieved, may be null. - * @return A list of TypeMirrors representing the interfaces directly implemented by the given TypeMirror. - * Returns an empty list if the input is null or no interfaces are directly implemented. - */ - @Nonnull - @Immutable - static List getTypeMirrorsOfInterfaces(TypeMirror type) { - return type == null ? emptyList() : findTypeMirrorsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of TypeMirrors representing the interfaces directly implemented by the given TypeElement. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include interfaces from superclasses or superinterfaces. - * - * @param type The TypeElement whose directly implemented interfaces are to be retrieved, may be null. - * @return A list of TypeMirrors representing the interfaces directly implemented by the given TypeElement. - * Returns an empty list if the input is null or no interfaces are directly implemented. - */ - @Nonnull - @Immutable - static List getTypeMirrorsOfInterfaces(TypeElement type) { - return type == null ? emptyList() : findTypeMirrorsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of TypeMirrors representing the interfaces directly implemented by the given TypeMirror. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include interfaces from superclasses or superinterfaces. - * - * @param type The TypeMirror whose directly implemented interfaces are to be retrieved, may be null. - * @param interfaceFilters Optional predicates to filter the resulting TypeMirrors. May be null or empty. - * @return A list of TypeMirrors representing the interfaces directly implemented by the given TypeMirror, - * filtered by the provided predicates. Returns an empty list if the input is null or no interfaces - * are directly implemented. - */ - @Nonnull - @Immutable - static List findTypeMirrorsOfInterfaces(TypeMirror type, Predicate... interfaceFilters) { - return type == null ? emptyList() : findTypeMirrorsOfInterfaces(ofTypeElement(type), interfaceFilters); - } - - /** - * Retrieves a list of TypeMirrors representing the interfaces directly implemented by the given TypeElement. - * This method only returns interfaces that are directly declared on the specified type, - * and does not include interfaces from superclasses or superinterfaces. - * - * @param type The TypeElement whose directly implemented interfaces are to be retrieved, may be null. - * @param interfaceFilters Optional predicates to filter the resulting TypeMirrors. May be null or empty. - * @return A list of TypeMirrors representing the interfaces directly implemented by the given TypeElement, - * filtered by the provided predicates. Returns an empty list if the input is null or no interfaces - * are directly implemented. - */ - @Nonnull - @Immutable - static List findTypeMirrorsOfInterfaces(TypeElement type, Predicate... interfaceFilters) { - if (type == null) { - return emptyList(); - } - List typeMirrors = getTypeElementsOfInterfaces(type).stream() - .map(TypeElement::asType) - .filter(and(interfaceFilters)) - .collect(toList()); - return typeMirrors.isEmpty() ? emptyList() : typeMirrors; - } - - /** - * Retrieves a list of TypeMirrors representing all interfaces implemented in the entire hierarchy - * of the given TypeMirror. This includes both directly and indirectly implemented interfaces from - * superclasses and superinterfaces. - * - * @param type The TypeMirror whose interface hierarchy is to be explored, may be null. - * @return A list of TypeMirrors representing all implemented interfaces in the hierarchy of - * the provided TypeMirror. Returns an empty list if the input is null or no interfaces - * are found in the hierarchy. - */ - @Nonnull - @Immutable - static List getAllTypeMirrorsOfInterfaces(TypeMirror type) { - return findAllTypeMirrorsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of TypeMirrors representing all interfaces implemented in the entire hierarchy - * of the given TypeElement. This includes both directly and indirectly implemented interfaces from - * superclasses and superinterfaces. - * - * @param type The TypeElement whose interface hierarchy is to be explored, may be null. - * @return A list of TypeMirrors representing all implemented interfaces in the hierarchy of - * the provided TypeElement. Returns an empty list if the input is null or no interfaces - * are found in the hierarchy. - */ - @Nonnull - @Immutable - static List getAllTypeMirrorsOfInterfaces(TypeElement type) { - return findAllTypeMirrorsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); - } - - /** - * Retrieves a list of TypeMirrors representing all interfaces implemented in the entire hierarchy - * of the given TypeMirror. This includes both directly and indirectly implemented interfaces from - * superclasses and superinterfaces. Optionally applies an array of predicates to filter the resulting TypeMirrors. - * - * @param type The TypeMirror whose interface hierarchy is to be explored, may be null. - * @param interfaceFilters Optional predicates to filter the resulting TypeMirrors. May be null or empty. - * @return A list of TypeMirrors representing all implemented interfaces in the hierarchy of - * the provided TypeMirror, filtered by the provided predicates. Returns an empty list if - * the input is null or no interfaces are found in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllTypeMirrorsOfInterfaces(TypeMirror type, Predicate... interfaceFilters) { - return type == null ? emptyList() : findAllTypeMirrorsOfInterfaces(ofTypeElement(type), interfaceFilters); - } - - /** - * Retrieves a list of TypeMirrors representing all interfaces implemented in the entire hierarchy - * of the given TypeElement. This includes both directly and indirectly implemented interfaces from - * superclasses and superinterfaces. Optionally applies an array of predicates to filter the resulting TypeMirrors. - * - * @param type The TypeElement whose interface hierarchy is to be explored, may be null. - * @param interfaceFilters Optional predicates to filter the resulting TypeMirrors. May be null or empty. - * @return A list of TypeMirrors representing all implemented interfaces in the hierarchy of - * the provided TypeElement, filtered by the provided predicates. Returns an empty list if - * the input is null or no interfaces are found in the hierarchy. - */ - @Nonnull - @Immutable - static List findAllTypeMirrorsOfInterfaces(TypeElement type, Predicate... interfaceFilters) { - if (type == null) { - return emptyList(); - } - List typeMirrors = getAllTypeElementsOfInterfaces(type).stream() - .map(TypeElement::asType) - .filter(and(interfaceFilters)) - .collect(toList()); - return typeMirrors.isEmpty() ? emptyList() : typeMirrors; - } - - /** - * Finds and returns the TypeMirror representing the specified interface type from the given Element. - * If the provided Element or interfaceType is null, returns null. - * - * @param type The Element to find the interface TypeMirror from. May be null. - * @param interfaceType The Type representing the interface to search for. May be null. - * @return The TypeMirror of the specified interface if found; otherwise, null. - */ - @Nullable - static TypeMirror findInterfaceTypeMirror(Element type, Type interfaceType) { - return findInterfaceTypeMirror(type, interfaceType.getTypeName()); - } - - /** - * Finds and returns the TypeMirror representing the specified interface type from the given TypeMirror. - * If the provided TypeMirror or interfaceType is null, returns null. - * - * @param type The TypeMirror to find the interface TypeMirror from. May be null. - * @param interfaceType The Type representing the interface to search for. May be null. - * @return The TypeMirror of the specified interface if found; otherwise, null. - */ - @Nullable - static TypeMirror findInterfaceTypeMirror(TypeMirror type, Type interfaceType) { - return findInterfaceTypeMirror(type, interfaceType.getTypeName()); - } - - /** - * Finds and returns the TypeMirror representing the specified interface type from the given Element. - * If the provided Element or interfaceClassName is null, returns null. - * - * @param type The Element to find the interface TypeMirror from. May be null. - * @param interfaceClassName The fully qualified class name of the interface to search for. May be null. - * @return The TypeMirror of the specified interface if found; otherwise, null. - */ - @Nullable - static TypeMirror findInterfaceTypeMirror(Element type, CharSequence interfaceClassName) { - return type == null ? null : findInterfaceTypeMirror(type.asType(), interfaceClassName); - } - - /** - * Finds and returns the TypeMirror representing the specified interface type from the given TypeMirror. - * If the provided TypeMirror or interfaceClassName is null, returns null. - * - *

This method searches through all interfaces implemented in the entire hierarchy of the given TypeMirror, - * including both directly and indirectly implemented interfaces from superclasses and superinterfaces. - * - * @param type The TypeMirror to find the interface TypeMirror from. May be null. - * @param interfaceClassName The fully qualified class name of the interface to search for. May be null. - * @return The TypeMirror of the specified interface if found; otherwise, null. - */ - @Nullable - static TypeMirror findInterfaceTypeMirror(TypeMirror type, CharSequence interfaceClassName) { - return filterFirst(getAllTypeMirrorsOfInterfaces(type), t -> isSameType(t, interfaceClassName)); - } - - /** - * Converts an array of Type objects to a list of TypeMirror instances using the provided ProcessingEnvironment. - * If the input array is null or empty, returns an empty list. - * - * @param processingEnv The ProcessingEnvironment used to resolve TypeMirrors. Must not be null. - * @param types The array of Type objects to convert. May contain null elements which will be ignored. - * @return A list of TypeMirror instances derived from the given Types. Returns an empty list if the input array is null or empty, - * or if no valid TypeMirror instances could be resolved. - */ - @Nonnull - @Immutable - static List getTypeMirrors(ProcessingEnvironment processingEnv, Type... types) { - if (isEmpty(types)) { - return emptyList(); - } - List typeMirrors = of(types) - .filter(Objects::nonNull) - .map(t -> getTypeMirror(processingEnv, t)) - .filter(Objects::nonNull) - .collect(toList()); - return typeMirrors.isEmpty() ? emptyList() : unmodifiableList(typeMirrors); - } - - /** - * Converts the given Type to a TypeMirror using the provided ProcessingEnvironment. - * - *

If the provided Type is null, this method returns null. Otherwise, it attempts - * to resolve the Type into a TypeElement and then retrieves its corresponding TypeMirror. - * - * @param processingEnv The ProcessingEnvironment used to resolve TypeMirrors. Must not be null. - * @param type The Type to convert to a TypeMirror. May be null. - * @return The resolved TypeMirror if available; otherwise, null. - */ - @Nullable - static TypeMirror getTypeMirror(ProcessingEnvironment processingEnv, Type type) { - TypeElement typeElement = getTypeElement(processingEnv, type); - return typeElement == null ? null : typeElement.asType(); - } - - /** - * Converts an array of Type objects to a list of TypeElement instances using the provided ProcessingEnvironment. - * If the input array is null or empty, returns an empty list. - * - * @param processingEnv The ProcessingEnvironment used to resolve TypeElements. Must not be null. - * @param types The array of Type objects to convert. May contain null elements which will be ignored. - * @return A list of TypeElement instances derived from the given Types. Returns an empty list if the input array is null or empty, - * or if no valid TypeElement instances could be resolved. - */ - @Nonnull - @Immutable - static List getTypeElements(ProcessingEnvironment processingEnv, Type... types) { - if (isEmpty(types)) { - return emptyList(); - } - List typeElements = of(types) - .filter(Objects::nonNull) - .map(t -> getTypeElement(processingEnv, t)) - .filter(Objects::nonNull) - .collect(toList()); - return typeElements.isEmpty() ? emptyList() : unmodifiableList(typeElements); - } - - /** - * Retrieves the TypeElement corresponding to the given Type using the provided ProcessingEnvironment. - * - *

If the provided {@link Type} is null, this method returns null. Otherwise, it attempts - * to resolve the Type into a TypeElement by first obtaining its fully qualified type name - * and then using the ElementUtils from the ProcessingEnvironment. - * - * @param processingEnv The ProcessingEnvironment used to resolve TypeElements. Must not be null. - * @param type The Type to convert to a TypeElement. May be null. - * @return The resolved TypeElement if available; otherwise, null. - */ - @Nullable - static TypeElement getTypeElement(ProcessingEnvironment processingEnv, Type type) { - return type == null ? null : getTypeElement(processingEnv, type.getTypeName()); - } - - /** - * Retrieves the TypeElement corresponding to the given TypeMirror using the provided ProcessingEnvironment. - * - *

If the provided {@link TypeMirror} is null, this method returns null. Otherwise, it attempts - * to resolve the TypeMirror into a TypeElement by first obtaining its fully qualified type name - * and then using the ElementUtils from the ProcessingEnvironment. - * - * @param processingEnv The ProcessingEnvironment used to resolve TypeElements. Must not be null. - * @param type The TypeMirror to convert to a TypeElement. May be null. - * @return The resolved TypeElement if available; otherwise, null. - */ - @Nullable - static TypeElement getTypeElement(ProcessingEnvironment processingEnv, TypeMirror type) { - return type == null ? null : getTypeElement(processingEnv, type.toString()); - } - - /** - * Retrieves the TypeElement corresponding to the given type name using the provided ProcessingEnvironment. - * - *

If the provided {@link ProcessingEnvironment} or typeName is null, this method returns null. - * Otherwise, it uses the ElementUtils from the ProcessingEnvironment to find and return the TypeElement. - * - * @param processingEnv The ProcessingEnvironment used to resolve TypeElements. Must not be null. - * @param typeName The fully qualified class name of the type to search for. May be null. - * @return The resolved TypeElement if available; otherwise, null. - */ - @Nullable - static TypeElement getTypeElement(ProcessingEnvironment processingEnv, CharSequence typeName) { - if (processingEnv == null || typeName == null) { - return null; - } - Elements elements = processingEnv.getElementUtils(); - return elements.getTypeElement(typeName); - } - - /** - * Retrieves the DeclaredType corresponding to the given Type using the provided ProcessingEnvironment. - * - *

If the provided {@link Type} is null, this method returns null. Otherwise, it attempts - * to resolve the Type into a DeclaredType by first obtaining its fully qualified type name - * and then using the ElementUtils from the ProcessingEnvironment to find the corresponding TypeElement. - * - * @param processingEnv The ProcessingEnvironment used to resolve DeclaredTypes. Must not be null. - * @param type The Type to convert to a DeclaredType. May be null. - * @return The resolved DeclaredType if available; otherwise, null. - */ - @Nullable - static DeclaredType getDeclaredType(ProcessingEnvironment processingEnv, Type type) { - return type == null ? null : getDeclaredType(processingEnv, type.getTypeName()); - } - - /** - * Retrieves the DeclaredType corresponding to the given TypeMirror using the provided ProcessingEnvironment. - * - *

If the provided {@link TypeMirror} is null, this method returns null. Otherwise, it attempts - * to resolve the TypeMirror into a DeclaredType by first obtaining its fully qualified type name - * and then using the ElementUtils from the ProcessingEnvironment to find the corresponding TypeElement. - * - * @param processingEnv The ProcessingEnvironment used to resolve DeclaredTypes. Must not be null. - * @param type The TypeMirror to convert to a DeclaredType. May be null. - * @return The resolved DeclaredType if available; otherwise, null. - */ - @Nullable - static DeclaredType getDeclaredType(ProcessingEnvironment processingEnv, TypeMirror type) { - return type == null ? null : getDeclaredType(processingEnv, type.toString()); - } - - /** - * Retrieves the DeclaredType corresponding to the given type name using the provided ProcessingEnvironment. - * - *

If the provided {@link ProcessingEnvironment} or typeName is null, this method returns null. - * Otherwise, it uses the ElementUtils from the ProcessingEnvironment to find and return the TypeElement, - * then converts it to a DeclaredType. - * - * @param processingEnv The ProcessingEnvironment used to resolve DeclaredTypes. Must not be null. - * @param typeName The fully qualified class name of the type to search for. May be null. - * @return The resolved DeclaredType if available; otherwise, null. - */ - @Nullable - static DeclaredType getDeclaredType(ProcessingEnvironment processingEnv, CharSequence typeName) { - return ofDeclaredType(getTypeElement(processingEnv, typeName)); - } - - /** - * Converts the given TypeMirror to its string representation. - * This method is typically used to obtain a readable and fully qualified name of the type, - * including any type parameters if present. - * - * @param type The TypeMirror to convert to a string, may be null. - * @return The string representation of the TypeMirror, or null if the input is null. - */ - @Nullable - static String toString(TypeMirror type) { - return getTypeName(type); - } - - /** - * Gets the fully qualified name of the given TypeMirror, including type parameters if present. - * - * @param type The TypeMirror to get the name from, may be null. - * @return The fully qualified name of the type including type parameters, or null if the input is null. - */ - @Nullable - static String getTypeName(TypeMirror type) { - if (type == null) { - return null; - } - TypeElement element = ofTypeElement(type); - if (element != null) { - List typeParameterElements = element.getTypeParameters(); - int typeParameterElementsSize = typeParameterElements.size(); - if (typeParameterElementsSize > 0) { - List typeMirrors = invokeMethod(type, "getTypeArguments"); - int size = typeMirrors.size(); - if (size > 0) { - StringBuilder typeBuilder = new StringBuilder(element.toString()); - typeBuilder.append(LESS_THAN_CHAR) - .append(typeMirrors) - .append(GREATER_THAN_CHAR); - return typeBuilder.toString(); - } - } - } - return type.toString(); - } - - /** - * Creates a TypeFinder instance for the specified TypeElement with configurable inclusion options. - * - *

This method allows searching for associated types based on the provided criteria: - * - *

    - *
  • {@code includeSelf} - Whether to include the type itself in the result.
  • - *
  • {@code includeHierarchicalTypes} - Whether to include types from the entire hierarchy (e.g., superclasses and interfaces).
  • - *
  • {@code includeSuperclass} - Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes.
  • - *
  • {@code includeInterfaces} - Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes.
  • - *
- * - * @param typeElement The TypeElement to start the search from. Must not be null. - * @param includeSelf Whether to include the type itself in the result. - * @param includeHierarchicalTypes Whether to include types from the entire hierarchy. - * @param includeSuperclass Whether to include direct or hierarchical superclasses. - * @param includeInterfaces Whether to include direct or hierarchical interfaces. - * @return A TypeFinder instance configured with the given parameters. - * @throws IllegalArgumentException if any parameter is invalid or if assertions fail. - */ - @Nonnull - static TypeFinder typeElementFinder(TypeElement typeElement, boolean includeSelf, - boolean includeHierarchicalTypes, boolean includeSuperclass, boolean includeInterfaces) { - return new TypeFinder(typeElement, TYPE_ELEMENT_GET_SUPERCLASS, TYPE_ELEMENT_GET_INTERFACES, includeSelf, - includeHierarchicalTypes, includeSuperclass, includeInterfaces); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/AbstractAnnotationProcessingTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/AbstractAnnotationProcessingTest.java deleted file mode 100644 index ce674e512..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/AbstractAnnotationProcessingTest.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor; - -import io.microsphere.annotation.processor.util.TypeUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.AnnotatedConstruct; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; - -import static io.microsphere.annotation.processor.util.ConstructorUtils.findConstructor; -import static io.microsphere.annotation.processor.util.FieldUtils.findField; -import static io.microsphere.annotation.processor.util.MethodUtils.findMethod; -import static io.microsphere.annotation.processor.util.TypeUtils.ofDeclaredType; -import static java.util.Collections.emptyList; -import static org.junit.jupiter.api.Assertions.assertSame; - -/** - * Abstract {@link Annotation} Processing Test case - * - * @author
Mercy - * @since 1.0.0 - */ -@ExtendWith(CompilerInvocationInterceptor.class) -public abstract class AbstractAnnotationProcessingTest { - - protected static final TypeMirror NULL_TYPE_MIRROR = null; - - protected static final TypeMirror[] EMPTY_TYPE_MIRROR_ARRAY = new TypeMirror[0]; - - protected static final TypeMirror[] NULL_TYPE_MIRROR_ARRAY = null; - - protected static final Collection[] EMPTY_COLLECTION_ARRAY = new Collection[0]; - - protected static final Collection NULL_COLLECTION = null; - - protected static final List NULL_LIST = null; - - protected static final Element NULL_ELEMENT = null; - - protected static final ElementKind NULL_ELEMENT_KIND = null; - - protected static final Element[] EMPTY_ELEMENT_ARRAY = new Element[0]; - - protected static final Element[] NULL_ELEMENT_ARRAY = null; - - protected static final TypeElement NULL_TYPE_ELEMENT = null; - - protected static final Type[] NULL_TYPE_ARRAY = null; - - protected static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; - - protected static final Type NULL_TYPE = null; - - protected static final ProcessingEnvironment NULL_PROCESSING_ENVIRONMENT = null; - - protected static final String NULL_STRING = null; - - protected static final String[] NULL_STRING_ARRAY = null; - - protected static final Class NULL_CLASS = null; - - protected static final Class[] NULL_CLASS_ARRAY = null; - - protected static final AnnotatedConstruct NULL_ANNOTATED_CONSTRUCT = null; - - protected static final Predicate[] NULL_PREDICATE_ARRAY = null; - - protected static final VariableElement NULL_FIELD = null; - - protected static final Modifier NULL_MODIFIER = null; - - protected static final Modifier[] NULL_MODIFIER_ARRAY = null; - - protected static final ExecutableElement NULL_METHOD = null; - - protected static final ExecutableElement[] NULL_METHOD_ARRAY = null; - - protected static final AnnotationMirror NULL_ANNOTATION_MIRROR = null; - - static ThreadLocal testInstanceHolder = new ThreadLocal<>(); - - protected RoundEnvironment roundEnv; - - protected ProcessingEnvironment processingEnv; - - protected Elements elements; - - protected Types types; - - protected Class testClass; - - protected String testClassName; - - protected TypeElement testTypeElement; - - protected TypeMirror testTypeMirror; - - protected DeclaredType testDeclaredType; - - @BeforeEach - final void setUp() { - testInstanceHolder.set(this); - } - - @AfterEach - final void tearDown() { - testInstanceHolder.remove(); - } - - protected void addCompiledClasses(Set> compiledClasses) { - } - - protected void beforeTest() { - this.testClass = TestServiceImpl.class; - this.testClassName = TestServiceImpl.class.getName(); - this.testTypeElement = getTypeElement(testClass); - this.testTypeMirror = this.testTypeElement.asType(); - this.testDeclaredType = ofDeclaredType(this.testTypeElement); - } - - protected void afterTest() { - } - - protected List getTypeMirrors(Type... types) { - return TypeUtils.getTypeMirrors(processingEnv, types); - } - - protected TypeMirror getTypeMirror(Type type) { - return TypeUtils.getTypeMirror(processingEnv, type); - } - - protected List getTypeElements(Type... types) { - return TypeUtils.getTypeElements(processingEnv, types); - } - - protected TypeElement getTypeElement(Type type) { - return TypeUtils.getTypeElement(processingEnv, type); - } - - protected VariableElement getField(Type type, String fieldName) { - TypeElement typeElement = getTypeElement(type); - return findField(typeElement, fieldName); - } - - protected ExecutableElement getMethod(Type type, String methodName, Type... parameterTypes) { - TypeElement typeElement = getTypeElement(type); - return findMethod(typeElement, methodName, parameterTypes); - } - - protected ExecutableElement getConstructor(Type type, Type... parameterTypes) { - TypeElement typeElement = getTypeElement(type); - return findConstructor(typeElement, parameterTypes); - } - - protected Element[] getElements(Type... types) { - return getTypeMirrors(types).stream().map(TypeUtils::ofTypeElement).toArray(Element[]::new); - } - - protected DeclaredType getDeclaredType(Type type) { - return TypeUtils.getDeclaredType(processingEnv, type); - } - - protected void assertEmptyList(List list) { - assertSame(emptyList(), list); - } - -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/AnnotationProcessingTestProcessor.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/AnnotationProcessingTestProcessor.java deleted file mode 100644 index 68e28ccd0..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/AnnotationProcessingTestProcessor.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.InvocationInterceptor; -import org.junit.jupiter.api.extension.ReflectiveInvocationContext; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.TypeElement; -import java.lang.reflect.Method; -import java.util.Set; - -import static io.microsphere.constants.SymbolConstants.WILDCARD; -import static javax.lang.model.SourceVersion.latestSupported; - -/** - * {@link AnnotationProcessingTestProcessor} - * - * @author Mercy - * @since 1.0.0 - */ -@SupportedAnnotationTypes(WILDCARD) -public class AnnotationProcessingTestProcessor extends AbstractProcessor { - - private final AbstractAnnotationProcessingTest abstractAnnotationProcessingTest; - private final InvocationInterceptor.Invocation invocation; - - private final ReflectiveInvocationContext invocationContext; - - private final ExtensionContext extensionContext; - - public AnnotationProcessingTestProcessor(AbstractAnnotationProcessingTest abstractAnnotationProcessingTest, InvocationInterceptor.Invocation invocation, - ReflectiveInvocationContext invocationContext, - ExtensionContext extensionContext) { - this.abstractAnnotationProcessingTest = abstractAnnotationProcessingTest; - this.invocation = invocation; - this.invocationContext = invocationContext; - this.extensionContext = extensionContext; - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (!roundEnv.processingOver()) { - prepare(roundEnv); - abstractAnnotationProcessingTest.beforeTest(); - try { - invocation.proceed(); - } catch (Throwable throwable) { - throw new RuntimeException(throwable); - } finally { - abstractAnnotationProcessingTest.afterTest(); - } - } - return false; - } - - protected void prepare(RoundEnvironment roundEnv) { - abstractAnnotationProcessingTest.roundEnv = roundEnv; - abstractAnnotationProcessingTest.processingEnv = super.processingEnv; - abstractAnnotationProcessingTest.elements = super.processingEnv.getElementUtils(); - abstractAnnotationProcessingTest.types = super.processingEnv.getTypeUtils(); - } - - @Override - public SourceVersion getSupportedSourceVersion() { - return latestSupported(); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/Compiler.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/Compiler.java deleted file mode 100644 index b8545abbc..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/Compiler.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor; - -import io.microsphere.logging.Logger; - -import javax.annotation.processing.Processor; -import javax.tools.JavaCompiler; -import javax.tools.JavaCompiler.CompilationTask; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import static io.microsphere.collection.CollectionUtils.addAll; -import static io.microsphere.collection.CollectionUtils.first; -import static io.microsphere.collection.ListUtils.newArrayList; -import static io.microsphere.collection.Lists.ofList; -import static io.microsphere.collection.SetUtils.newLinkedHashSet; -import static io.microsphere.constants.FileConstants.JAVA_EXTENSION; -import static io.microsphere.constants.ProtocolConstants.FILE_PROTOCOL; -import static io.microsphere.constants.SymbolConstants.DOT_CHAR; -import static io.microsphere.io.scanner.SimpleFileScanner.INSTANCE; -import static io.microsphere.logging.LoggerFactory.getLogger; -import static io.microsphere.util.ClassUtils.getTypeName; -import static io.microsphere.util.StringUtils.substringBefore; -import static java.io.File.separatorChar; -import static java.util.Collections.singleton; -import static javax.tools.StandardLocation.CLASS_OUTPUT; -import static javax.tools.StandardLocation.SOURCE_OUTPUT; -import static javax.tools.StandardLocation.SOURCE_PATH; -import static javax.tools.ToolProvider.getSystemJavaCompiler; - -/** - * The Java Compiler - * - * @author Mercy - * @since 1.0.0 - */ -public class Compiler { - - private static final Logger logger = getLogger(Compiler.class); - - private final Set sourcePaths; - - private final JavaCompiler javaCompiler; - - private final StandardJavaFileManager javaFileManager; - - private final Set processors = new LinkedHashSet<>(); - - public Compiler() throws IOException { - this(defaultTargetDirectory()); - } - - public Compiler(File targetDirectory) throws IOException { - this(defaultSourceDirectory(), targetDirectory); - } - - public Compiler(File defaultSourceDirectory, File targetDirectory) throws IOException { - this.sourcePaths = newLinkedHashSet(defaultSourceDirectory); - this.javaCompiler = getSystemJavaCompiler(); - this.javaFileManager = javaCompiler.getStandardFileManager(null, null, null); - this.javaFileManager.setLocation(SOURCE_PATH, sourcePaths); - this.javaFileManager.setLocation(CLASS_OUTPUT, singleton(targetDirectory)); - this.javaFileManager.setLocation(SOURCE_OUTPUT, singleton(targetDirectory)); - } - - static File defaultSourceDirectory() { - return detectSourcePath(Compiler.class); - } - - static File defaultRootDirectory() { - return detectRootDirectory(Compiler.class); - } - - static File defaultTargetDirectory() { - File dir = new File(defaultRootDirectory(), "target/generated-classes"); - dir.mkdirs(); - return dir; - } - - static File detectSourcePath(Class sourceClass) { - File rootDirectory = detectRootDirectory(sourceClass); - String javaSourceFileRelativePath = resolveJavaSourceFileRelativePath(sourceClass); - - Set sourceFiles = INSTANCE.scan(rootDirectory, true, - file -> file.getAbsolutePath().endsWith(javaSourceFileRelativePath)); - if (sourceFiles.isEmpty()) { - if (logger.isTraceEnabled()) { - logger.trace("The source files of class[name : '{}'] can't be found in the root directory[path :'{}']", - getTypeName(sourceClass), rootDirectory.getAbsolutePath()); - } - return null; - } - - File sourceFile = first(sourceFiles); - String javaSourceFilePath = sourceFile.getAbsolutePath(); - String javaSourcePath = substringBefore(javaSourceFilePath, javaSourceFileRelativePath); - File sourcePath = new File(javaSourcePath); - - if (logger.isTraceEnabled()) { - logger.trace("The source file[path : '{}] of class[name : '{}'] was found in the source directory[path :'{}']", - sourceFile.getAbsolutePath(), getTypeName(sourceClass), sourcePath.getAbsolutePath()); - } - - return sourcePath.exists() ? sourcePath : null; - } - - static File detectRootDirectory(Class sourceClass) { - File classPath = detectClassPath(sourceClass); - // classPath : "${rootDirectory}/target/classes" - File rootDirectory = classPath.getParentFile().getParentFile(); - if (logger.isTraceEnabled()) { - logger.trace("The root directory[path : '{}'] was found by the source class[name : '{}']", - rootDirectory.getAbsolutePath(), getTypeName(sourceClass)); - } - return rootDirectory; - } - - static File detectClassPath(Class sourceClass) { - URL classFileURL = sourceClass.getProtectionDomain().getCodeSource().getLocation(); - if (FILE_PROTOCOL.equals(classFileURL.getProtocol())) { - return new File(classFileURL.getPath()); - } else { - throw new RuntimeException("No support"); - } - } - - static String resolveJavaSourceFileRelativePath(Class sourceClass) { - return sourceClass.getName().replace(DOT_CHAR, separatorChar).concat(JAVA_EXTENSION); - } - - public Compiler sourcePaths(File... sourcePaths) { - addAll(this.sourcePaths, sourcePaths); - return this; - } - - public Compiler sourcePaths(Iterable> sourceClasses) { - for (Class sourceClass : sourceClasses) { - sourcePath(sourceClass); - } - return this; - } - - public Compiler sourcePath(Class sourceClass) { - File sourcePath = detectSourcePath(sourceClass); - if (sourcePath != null) { - return sourcePaths(sourcePath); - } - return this; - } - - public Compiler processors(Processor... processors) { - addAll(this.processors, processors); - return this; - } - - private Iterable getJavaFileObjects(Class... sourceClasses) { - int size = sourceClasses == null ? 0 : sourceClasses.length; - List javaSourceFiles = newArrayList(size); - for (int i = 0; i < size; i++) { - File javaSourceFile = searchJavaSourceFile(sourceClasses[i]); - if (javaSourceFile != null) { - javaSourceFiles.add(javaSourceFile); - } - } - return javaFileManager.getJavaFileObjects(javaSourceFiles.toArray(new File[0])); - } - - private File searchJavaSourceFile(Class sourceClass) { - String javaSourceFilePath = resolveJavaSourceFileRelativePath(sourceClass); - for (File sourceDirectory : sourcePaths) { - File javaSourceFile = new File(sourceDirectory, javaSourceFilePath); - if (javaSourceFile.exists()) { - return javaSourceFile; - } - } - return null; - } - - public boolean compile(Class... sourceClasses) { - CompilationTask task = javaCompiler.getTask(null, this.javaFileManager, null, - ofList("-parameters", "-Xlint:unchecked", "-nowarn", "-Xlint:deprecation"), -// null, - null, getJavaFileObjects(sourceClasses)); - if (!processors.isEmpty()) { - task.setProcessors(processors); - } - return task.call(); - } - - public JavaCompiler getJavaCompiler() { - return javaCompiler; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/CompilerInvocationInterceptor.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/CompilerInvocationInterceptor.java deleted file mode 100644 index 966e6defa..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/CompilerInvocationInterceptor.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.InvocationInterceptor; -import org.junit.jupiter.api.extension.ReflectiveInvocationContext; - -import javax.annotation.processing.Processor; -import java.lang.reflect.Method; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import static io.microsphere.annotation.processor.AbstractAnnotationProcessingTest.testInstanceHolder; -import static io.microsphere.util.ServiceLoaderUtils.loadServicesList; - - -/** - * {@link InvocationInterceptor} based on Java {@link Compiler} - * - * @author Mercy - * @since 1.0.0 - */ -public class CompilerInvocationInterceptor implements InvocationInterceptor { - - @Override - public void interceptTestMethod(Invocation invocation, - ReflectiveInvocationContext invocationContext, - ExtensionContext extensionContext) throws Throwable { - Set> compiledClasses = new LinkedHashSet<>(); - AbstractAnnotationProcessingTest abstractAnnotationProcessingTest = testInstanceHolder.get(); - compiledClasses.add(getClass()); - abstractAnnotationProcessingTest.addCompiledClasses(compiledClasses); - Compiler compiler = new Compiler(); - compiler.sourcePaths(compiledClasses); - List processors = new LinkedList<>(loadServicesList(Processor.class, this.getClass().getClassLoader())); - processors.add(new AnnotationProcessingTestProcessor(abstractAnnotationProcessingTest, invocation, invocationContext, extensionContext)); - compiler.processors(processors.toArray(new Processor[0])); - compiler.compile(compiledClasses.toArray(new Class[0])); - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/DefaultTestService.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/DefaultTestService.java deleted file mode 100644 index 7e643daab..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/DefaultTestService.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor; - - -import io.microsphere.annotation.processor.model.Model; - -import java.util.concurrent.TimeUnit; - -/** - * {@link TestService} Implementation - * - * @author Mercy - * @since 1.0.0 - */ -public class DefaultTestService implements TestService { - - private String name; - - @Override - public String echo(String message) { - return "[ECHO] " + message; - } - - @Override - public Model model(Model model) { - return model; - } - - @Override - public String testPrimitive(boolean z, int i) { - return null; - } - - @Override - public Model testEnum(TimeUnit timeUnit) { - return null; - } - - @Override - public String testArray(String[] strArray, int[] intArray, Model[] modelArray) { - return null; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/GenericTestService.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/GenericTestService.java deleted file mode 100644 index 449b73611..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/GenericTestService.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor; - - -import java.util.EventListener; - -/** - * {@link TestService} Implementation - * - * @author Mercy - * @since 1.0.0 - */ -public class GenericTestService extends DefaultTestService implements TestService, EventListener { - @Override - public String echo(String message) { - return "[ECHO] " + message; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestAnnotation.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestAnnotation.java deleted file mode 100644 index 4d35d6f82..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestAnnotation.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor; - -import io.microsphere.annotation.ConfigurationProperty; -import io.microsphere.annotation.Since; - -import java.lang.annotation.Annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.util.concurrent.TimeUnit; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.util.concurrent.TimeUnit.DAYS; - -/** - * The {@link Annotation} for testing - * - * @author Mercy - * @see Annotation - * @since 1.0.0 - */ -@Retention(RUNTIME) -@Target(TYPE) -@Documented -public @interface TestAnnotation { - - boolean z() default false; - - char c() default 'a'; - - byte b() default 1; - - short s() default 2; - - int i() default 3; - - long l() default 4L; - - float f() default 5.0f; - - double d() default 6.0d; - - String string() default "string"; - - Class type() default String.class; - - Class[] types() default {String.class, Integer.class}; - - TimeUnit timeUnit() default DAYS; - - Since since(); - - ConfigurationProperty[] properties() default {}; - -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestService.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestService.java deleted file mode 100644 index 860748c13..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestService.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor; - -import io.microsphere.annotation.processor.model.Model; - -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import java.util.concurrent.TimeUnit; - -/** - * Test Service - * - * @author Mercy - * @since 1.0.0 - */ -@Path("/echo") -public interface TestService { - - @GET - String echo(@PathParam("message") @DefaultValue("mercyblitz") String message); - - @POST - Model model(@PathParam("model") Model model); - - // Test primitive - @PUT - String testPrimitive(boolean z, int i); - - // Test enumeration - @PUT - Model testEnum(TimeUnit timeUnit); - - // Test Array - @GET - String testArray(String[] strArray, int[] intArray, Model[] modelArray); -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestServiceImpl.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestServiceImpl.java deleted file mode 100644 index e587e888e..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/TestServiceImpl.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor; - -import io.microsphere.annotation.ConfigurationProperty; -import io.microsphere.annotation.Since; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScans; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; - -import javax.xml.ws.ServiceMode; -import java.io.Serializable; - -import static java.util.concurrent.TimeUnit.HOURS; -import static org.springframework.context.annotation.FilterType.ASPECTJ; -import static org.springframework.context.annotation.ScopedProxyMode.INTERFACES; - -/** - * @author Mercy - * @since 1.0.0 - */ -@Service("testService") -@ServiceMode -@ComponentScans(value = { - @ComponentScan( - basePackages = "io.microsphere.annotation.processor.model", - scopedProxy = INTERFACES - ), - @ComponentScan( - basePackages = "io.microsphere.annotation.processor.util", - includeFilters = { - @ComponentScan.Filter( - type = ASPECTJ, - classes = {Object.class, CharSequence.class} - ) - }) -}) -@TestAnnotation( - z = true, - c = 'b', - b = 1, - s = 1, - i = 1, - l = 1, - f = 1, - d = 1, - string = "testService", - type = GenericTestService.class, - types = {TestService.class, AutoCloseable.class, Serializable.class}, - timeUnit = HOURS, - since = @Since("1.0.0"), - properties = { - @ConfigurationProperty(name = "key", type = String.class, defaultValue = "default-value", required = true, description = "description"), - @ConfigurationProperty(name = "key2", type = Integer.class, defaultValue = "default-value2", required = true, description = "description2"), - @ConfigurationProperty(name = "key3", type = Class.class, defaultValue = "default-value3", required = true, description = "description3") - } -) -public class TestServiceImpl extends GenericTestService implements TestService, AutoCloseable, Serializable { - - @Autowired - private ApplicationContext context; - - private Environment environment; - - public TestServiceImpl() { - this(null); - } - - public TestServiceImpl(@Autowired Environment environment) { - this.environment = environment; - } - - @Override - @Cacheable(cacheNames = {"cache-1", "cache-2"}) - public String echo(String message) { - return "[ECHO] " + message; - } - - @Override - public void close() throws Exception { - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Ancestor.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Ancestor.java deleted file mode 100644 index 9a749d056..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Ancestor.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -import java.io.Serializable; - -/** - * Ancestor - */ -public class Ancestor implements Serializable { - - private boolean z; - - public boolean isZ() { - return z; - } - - public void setZ(boolean z) { - this.z = z; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/ArrayTypeModel.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/ArrayTypeModel.java deleted file mode 100644 index fc6c057f3..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/ArrayTypeModel.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -/** - * Array Type Model - * - * @since 1.0.0 - */ -public class ArrayTypeModel { - - private int[] integers; // Primitive type array - - private String[] strings; // Simple type array - - private PrimitiveTypeModel[] primitiveTypeModels; // Complex type array - - private Model[] models; // Hierarchical Complex type array - - private Color[] colors; // Enum type array - -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/CollectionTypeModel.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/CollectionTypeModel.java deleted file mode 100644 index 3f5426361..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/CollectionTypeModel.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -import java.util.Collection; -import java.util.Deque; -import java.util.List; -import java.util.Queue; -import java.util.Set; - -/** - * {@link Collection} Type Model - * - * @since 1.0.0 - */ -public class CollectionTypeModel { - - private Collection strings; // The composite element is simple type - - private List colors; // The composite element is Enum type - - private Queue primitiveTypeModels; // The composite element is POJO type - - private Deque models; // The composite element is hierarchical POJO type - - private Set modelArrays; // The composite element is hierarchical POJO type - -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Color.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Color.java deleted file mode 100644 index 177dfaffc..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Color.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -/** - * Color enumeration - * - * @since 1.0.0 - */ -public enum Color { - - RED(1), - YELLOW(2), - BLUE(3); - - private final int value; - - Color(int value) { - this.value = value; - } - - @Override - public String toString() { - return "Color{" + - "value=" + value + - "} " + super.toString(); - } - - public int getValue() { - return value; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/ConfigurationPropertyModel.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/ConfigurationPropertyModel.java deleted file mode 100644 index 9a6505460..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/ConfigurationPropertyModel.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model; - -import io.microsphere.annotation.ConfigurationProperty; - -/** - * {@link ConfigurationProperty} Model - * - * @author Mercy - * @see ConfigurationProperty - * @since 1.0.0 - */ -public class ConfigurationPropertyModel { - - @ConfigurationProperty(name = "microsphere.annotation.processor.model.name") - private String name; - - @ConfigurationProperty(name = "microsphere.annotation.processor.model.type") - private Class type; - - @ConfigurationProperty(name = "microsphere.annotation.processor.model.default-value") - private String defaultValue; - - @ConfigurationProperty(name = "microsphere.annotation.processor.model.required") - private boolean required; - - @ConfigurationProperty(name = "microsphere.annotation.processor.model.description") - private String description; - - -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/MapTypeModel.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/MapTypeModel.java deleted file mode 100644 index 3c9fc58c4..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/MapTypeModel.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -import java.util.HashMap; -import java.util.Map; -import java.util.NavigableMap; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * {@link Map} Type model - * - * @since 1.0.0 - */ -public class MapTypeModel { - - private Map strings; // The composite element is simple type - - private SortedMap colors; // The composite element is Enum type - - private NavigableMap primitiveTypeModels; // The composite element is POJO type - - private HashMap models; // The composite element is hierarchical POJO type - - private TreeMap modelArrays; // The composite element is hierarchical POJO type -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Model.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Model.java deleted file mode 100644 index 08348e513..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Model.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.concurrent.TimeUnit; - -/** - * Model Object - */ -public class Model extends Parent { - - private float f; - - private double d; - - private TimeUnit tu; - - private String str; - - private BigInteger bi; - - private BigDecimal bd; - - public float getF() { - return f; - } - - public void setF(float f) { - this.f = f; - } - - public double getD() { - return d; - } - - public void setD(double d) { - this.d = d; - } - - public TimeUnit getTu() { - return tu; - } - - public void setTu(TimeUnit tu) { - this.tu = tu; - } - - public String getStr() { - return str; - } - - public void setStr(String str) { - this.str = str; - } - - public BigInteger getBi() { - return bi; - } - - public void setBi(BigInteger bi) { - this.bi = bi; - } - - public BigDecimal getBd() { - return bd; - } - - public void setBd(BigDecimal bd) { - this.bd = bd; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Parent.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Parent.java deleted file mode 100644 index b124fb814..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/Parent.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -/** - * Parent - */ -public class Parent extends Ancestor { - - private byte b; - - private short s; - - private int i; - - private long l; - - public byte getB() { - return b; - } - - public void setB(byte b) { - this.b = b; - } - - public short getS() { - return s; - } - - public void setS(short s) { - this.s = s; - } - - public int getI() { - return i; - } - - public void setI(int i) { - this.i = i; - } - - public long getL() { - return l; - } - - public void setL(long l) { - this.l = l; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/PrimitiveTypeModel.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/PrimitiveTypeModel.java deleted file mode 100644 index 2bbe5372b..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/PrimitiveTypeModel.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -/** - * Primitive Type model - * - * @since 1.0.0 - */ -public class PrimitiveTypeModel { - - private boolean z; - - private byte b; - - private char c; - - private short s; - - private int i; - - private long l; - - private float f; - - private double d; - - public boolean isZ() { - return z; - } - - public byte getB() { - return b; - } - - public char getC() { - return c; - } - - public short getS() { - return s; - } - - public int getI() { - return i; - } - - public long getL() { - return l; - } - - public float getF() { - return f; - } - - public double getD() { - return d; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/SimpleTypeModel.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/SimpleTypeModel.java deleted file mode 100644 index 4522d5554..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/SimpleTypeModel.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.model; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Date; - -/** - * Simple Type model - * - * @since 1.0.0 - */ -public class SimpleTypeModel { - - private Void v; - - private Boolean z; - - private Character c; - - private Byte b; - - private Short s; - - private Integer i; - - private Long l; - - private Float f; - - private Double d; - - private String str; - - private BigDecimal bd; - - private BigInteger bi; - - private Date dt; - - private int invalid; - - public Void getV() { - return v; - } - - public void setV(Void v) { - this.v = v; - } - - public Boolean getZ() { - return z; - } - - public void setZ(Boolean z) { - this.z = z; - } - - public Character getC() { - return c; - } - - public void setC(Character c) { - this.c = c; - } - - public Byte getB() { - return b; - } - - public void setB(Byte b) { - this.b = b; - } - - public Short getS() { - return s; - } - - public void setS(Short s) { - this.s = s; - } - - public Integer getI() { - return i; - } - - public void setI(Integer i) { - this.i = i; - } - - public Long getL() { - return l; - } - - public void setL(Long l) { - this.l = l; - } - - public Float getF() { - return f; - } - - public void setF(Float f) { - this.f = f; - } - - public Double getD() { - return d; - } - - public void setD(Double d) { - this.d = d; - } - - public String getStr() { - return str; - } - - public void setStr(String str) { - this.str = str; - } - - public BigDecimal getBd() { - return bd; - } - - public void setBd(BigDecimal bd) { - this.bd = bd; - } - - public BigInteger getBi() { - return bi; - } - - public void setBi(BigInteger bi) { - this.bi = bi; - } - - public Date getDt() { - return dt; - } - - public void setDt(Date dt) { - this.dt = dt; - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/StringArrayList.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/StringArrayList.java deleted file mode 100644 index f59710d58..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/StringArrayList.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model; - -import java.util.ArrayList; - -/** - * String type {@link ArrayList} - * - * @author Mercy - * @see ArrayList - * @since 1.0.0 - */ -public class StringArrayList extends ArrayList { -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/element/StringAnnotationValueTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/element/StringAnnotationValueTest.java deleted file mode 100644 index 6e96fb50a..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/element/StringAnnotationValueTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model.element; - - -import io.microsphere.annotation.processor.model.util.ResolvableAnnotationValueVisitor; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * {@link StringAnnotationValue} Test - * - * @author Mercy - * @see StringAnnotationValue - * @since 1.0.0 - */ -class StringAnnotationValueTest { - - private StringAnnotationValue value; - - @BeforeEach - void setUp() { - this.value = new StringAnnotationValue("testing"); - } - - @Test - void testGetValue() { - assertEquals("testing", value.getValue()); - } - - @Test - void testAccept() { - ResolvableAnnotationValueVisitor visitor = new ResolvableAnnotationValueVisitor(); - assertEquals("testing", value.accept(visitor, null)); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/JSONAnnotationValueVisitorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/JSONAnnotationValueVisitorTest.java deleted file mode 100644 index 094b1460c..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/JSONAnnotationValueVisitorTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model.util; - - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.TestAnnotation; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.ExecutableElement; -import java.util.Map; - -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValue; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValues; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; - -/** - * {@link JSONAnnotationValueVisitor} Test - * - * @author Mercy - * @see JSONAnnotationValueVisitor - * @since 1.0.0 - */ -class JSONAnnotationValueVisitorTest extends AbstractAnnotationProcessingTest { - - private StringBuilder jsonBuilder; - - private JSONAnnotationValueVisitor visitor; - - private Map testAnnotationAttributes; - - protected void beforeTest() { - super.beforeTest(); - this.jsonBuilder = new StringBuilder(); - this.visitor = new JSONAnnotationValueVisitor(jsonBuilder); - this.testAnnotationAttributes = getElementValues(testTypeElement, TestAnnotation.class); - } - - @Test - void testVisitBoolean() { - testVisit("z", "\"z\":true"); - } - - @Test - void testVisitByte() { - testVisit("b", "\"b\":1"); - } - - @Test - void testVisitChar() { - testVisit("c", "\"c\":\"b\""); - } - - @Test - void testVisitDouble() { - testVisit("d", "\"d\":1.0"); - } - - @Test - void testVisitFloat() { - testVisit("f", "\"f\":1.0"); - } - - @Test - void testVisitInt() { - testVisit("i", "\"i\":1"); - } - - @Test - void testVisitLong() { - testVisit("l", "\"l\":1"); - } - - @Test - void testVisitShort() { - testVisit("s", "\"s\":1"); - } - - @Test - void testVisitString() { - testVisit("string", "\"string\":\"testService\""); - } - - @Test - void testVisitType() { - testVisit("type", "\"type\":\"io.microsphere.annotation.processor.GenericTestService\""); - } - - @Test - void testVisitEnumConstant() { - testVisit("timeUnit", "\"timeUnit\":\"HOURS\""); - } - - @Test - void testVisitAnnotation() { - testVisit("since", "\"since\":{\"module\":\"\",\"value\":\"1.0.0\"}"); - } - - @Test - void testVisitArray() { - testVisit("properties", "\"properties\":[{\"name\":\"key\",\"type\":\"java.lang.String\",\"defaultValue\":\"default-value\",\"required\":true,\"description\":\"description\",\"source\":[]},{\"name\":\"key2\",\"type\":\"java.lang.Integer\",\"defaultValue\":\"default-value2\",\"required\":true,\"description\":\"description2\",\"source\":[]},{\"name\":\"key3\",\"type\":\"java.lang.Class\",\"defaultValue\":\"default-value3\",\"required\":true,\"description\":\"description3\",\"source\":[]}]"); - } - - @Test - void testVisitUnknown() { - assertSame(this.jsonBuilder, visitor.visitUnknown(null, null)); - } - - void testVisit(String attributeName, String expectedJson) { - Map.Entry elementValue = getElementValue(this.testAnnotationAttributes, attributeName); - ExecutableElement attributeMethod = elementValue.getKey(); - AnnotationValue annotationValue = elementValue.getValue(); - StringBuilder jsonBuilder = visitor.visit(annotationValue, attributeMethod); - assertEquals(expectedJson, jsonBuilder.toString()); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/JSONElementVisitorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/JSONElementVisitorTest.java deleted file mode 100644 index 81a9a5adf..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/JSONElementVisitorTest.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model.util; - - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.TestAnnotation; -import io.microsphere.annotation.processor.model.Color; -import io.microsphere.annotation.processor.model.StringArrayList; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.TypeParameterElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; -import java.io.Serializable; -import java.util.List; - -import static io.microsphere.annotation.processor.util.TypeUtils.ofTypeElement; -import static java.lang.Boolean.TRUE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * {@link JSONElementVisitor} Test - * - * @author Mercy - * @see JSONElementVisitor - * @since 1.0.0 - */ -class JSONElementVisitorTest extends AbstractAnnotationProcessingTest { - - private boolean supported; - - private StringBuilder jsonBuilder; - - private JSONElementVisitor visitor; - - - @BeforeEach - void setUp() { - this.supported = true; - this.jsonBuilder = new StringBuilder(); - this.visitor = new JSONElementVisitor() { - - @Override - protected boolean supports(Element e) { - super.supports(e); - return supported; - } - - @Override - protected boolean doVisitPackage(PackageElement e, StringBuilder jsonBuilder) { - super.doVisitPackage(e, jsonBuilder); - jsonBuilder.append("visitPackage"); - return TRUE; - } - - @Override - protected boolean doVisitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { - super.doVisitTypeParameter(e, jsonBuilder); - jsonBuilder.append("visitTypeParameter"); - return TRUE; - } - - @Override - public Boolean visitVariableAsEnumConstant(VariableElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitVariableAsEnumConstant"); - return TRUE; - } - - @Override - public Boolean visitVariableAsField(VariableElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitVariableAsField"); - return TRUE; - } - - @Override - public Boolean visitVariableAsParameter(VariableElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitVariableAsParameter"); - return TRUE; - } - - @Override - public Boolean visitExecutableAsConstructor(ExecutableElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitExecutableAsConstructor"); - return TRUE; - } - - @Override - public Boolean visitExecutableAsMethod(ExecutableElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitExecutableAsMethod"); - return TRUE; - } - - @Override - public Boolean visitTypeAsInterface(TypeElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitTypeAsInterface"); - return TRUE; - } - - @Override - public Boolean visitTypeAsEnum(TypeElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitTypeAsEnum"); - return TRUE; - } - - @Override - public Boolean visitTypeAsClass(TypeElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitTypeAsClass"); - return TRUE; - } - - @Override - public Boolean visitTypeAsAnnotationType(TypeElement e, StringBuilder stringBuilder) { - jsonBuilder.append("visitTypeAsAnnotationType"); - return TRUE; - } - }; - } - - @Test - void testVisitPackage() { - assertTrue(visitor.visitPackage(this.elements.getPackageElement("io.microsphere.annotation.processor.model.util"), jsonBuilder)); - assertJson("visitPackage"); - } - - @Test - void testVisitVariableOnUnsupported() { - supported = false; - assertFalse(visitor.visitVariable(null, jsonBuilder)); - } - - @Test - void testVisitVariableAsEnumConstant() { - VariableElement element = getField(Color.class, "RED"); - assertTrue(visitor.visitVariable(element, jsonBuilder)); - assertJson("visitVariableAsEnumConstant"); - - } - - @Test - void testVisitExecutableAsField() { - VariableElement element = getField(testClass, "context"); - assertTrue(visitor.visitVariable(element, jsonBuilder)); - assertJson("visitVariableAsField"); - } - - @Test - void testVisitVariableAsParameter() { - ExecutableElement method = getMethod(testClass, "echo", String.class); - for (VariableElement parameter : method.getParameters()) { - assertTrue(visitor.visitVariable(parameter, jsonBuilder)); - assertJson("visitVariableAsParameter"); - } - } - - @Test - void testVisitExecutableOnUnsupported() { - supported = false; - assertFalse(visitor.visitExecutable(null, jsonBuilder)); - } - - @Test - void testVisitExecutableAsConstructor() { - ExecutableElement constructor = getConstructor(testClass); - assertTrue(visitor.visitExecutable(constructor, jsonBuilder)); - assertJson("visitExecutableAsConstructor"); - } - - @Test - void testVisitExecutableAsMethod() { - ExecutableElement method = getMethod(testClass, "echo", String.class); - assertTrue(visitor.visitExecutable(method, jsonBuilder)); - assertJson("visitExecutableAsMethod"); - } - - @Test - void testVisitTypeOnUnsupported() { - supported = false; - TypeElement typeElement = getTypeElement(Serializable.class); - assertFalse(visitor.visitType(typeElement, jsonBuilder)); - } - - @Test - void testVisitTypeAsInterface() { - TypeElement typeElement = getTypeElement(Serializable.class); - assertTrue(visitor.visitType(typeElement, jsonBuilder)); - assertJson("visitTypeAsInterface"); - } - - @Test - void testVisitTypeAsEnum() { - TypeElement typeElement = getTypeElement(Color.class); - assertTrue(visitor.visitType(typeElement, jsonBuilder)); - assertTrue(jsonBuilder.toString().startsWith("visitTypeAsEnum")); - } - - @Test - void testVisitTypeAsClass() { - TypeElement typeElement = getTypeElement(testClass); - assertTrue(visitor.visitType(typeElement, jsonBuilder)); - assertTrue(jsonBuilder.toString().startsWith("visitTypeAsClass")); - } - - @Test - void testVisitTypeAsAnnotationType() { - TypeElement typeElement = getTypeElement(TestAnnotation.class); - assertTrue(visitor.visitType(typeElement, jsonBuilder)); - assertTrue(jsonBuilder.toString().startsWith("visitTypeAsAnnotationType")); - } - - @Test - void testVisitTypeParameterOnUnsupported() { - supported = false; - assertFalse(visitor.visitTypeParameter(null, jsonBuilder)); - } - - @Test - void testVisitTypeParameter() { - TypeElement typeElement = getTypeElement(StringArrayList.class); - TypeMirror superclass = typeElement.getSuperclass(); - TypeElement superTypeElement = ofTypeElement(superclass); - List typeParameters = superTypeElement.getTypeParameters(); - for (TypeParameterElement typeParameter : typeParameters) { - assertTrue(visitor.visitTypeParameter(typeParameter, jsonBuilder)); - assertJson("visitTypeParameter"); - } - } - - void assertJson(String expected) { - assertEquals(expected, jsonBuilder.toString()); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/ResolvableAnnotationValueVisitorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/ResolvableAnnotationValueVisitorTest.java deleted file mode 100644 index 0f0ef36d7..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/model/util/ResolvableAnnotationValueVisitorTest.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.model.util; - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.GenericTestService; -import io.microsphere.annotation.processor.TestAnnotation; -import io.microsphere.annotation.processor.TestService; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.AnnotationValueVisitor; -import javax.lang.model.element.ExecutableElement; -import java.io.Serializable; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValue; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValues; -import static java.util.Objects.deepEquals; -import static java.util.concurrent.TimeUnit.HOURS; -import static java.util.stream.Stream.of; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * {@link ResolvableAnnotationValueVisitor} Test - * - * @author Mercy - * @see ResolvableAnnotationValueVisitor - * @since 1.0.0 - */ -class ResolvableAnnotationValueVisitorTest extends AbstractAnnotationProcessingTest { - - private ResolvableAnnotationValueVisitor visitor; - - private ResolvableAnnotationValueVisitor visitor1; - - private ResolvableAnnotationValueVisitor visitor2; - - static final boolean BOOLEAN_VALUE = true; - - static final byte BYTE_VALUE = 1; - - static final char CHAR_VALUE = 'b'; - - static final double DOUBLE_VALUE = 1.0D; - - static final float FLOAT_VALUE = 1.0F; - - static final int INT_VALUE = 1; - - static final long LONG_VALUE = 1L; - - static final short SHORT_VALUE = 1; - - static final String STRING_VALUE = "testService"; - - static final Class TYPE_VALUE = GenericTestService.class; - - static final Class[] TYPES_VALUE = {TestService.class, AutoCloseable.class, Serializable.class}; - - private Map testAnnotationAttributes; - - protected void beforeTest() { - super.beforeTest(); - this.visitor = new ResolvableAnnotationValueVisitor(); - this.visitor1 = new ResolvableAnnotationValueVisitor(true); - this.visitor2 = new ResolvableAnnotationValueVisitor(true, true); - this.testAnnotationAttributes = getElementValues(testTypeElement, TestAnnotation.class); - } - - @Test - void testVisitBoolean() { - assertEquals(BOOLEAN_VALUE, visitor.visitBoolean(BOOLEAN_VALUE, null)); - assertVisit(this.visitor, "z", BOOLEAN_VALUE); - assertVisit(this.visitor1, "z", BOOLEAN_VALUE); - assertVisit(this.visitor2, "z", BOOLEAN_VALUE); - } - - @Test - void testVisitByte() { - assertEquals(BYTE_VALUE, visitor.visitByte(BYTE_VALUE, null)); - assertVisit(this.visitor, "b", BYTE_VALUE); - assertVisit(this.visitor1, "b", BYTE_VALUE); - assertVisit(this.visitor2, "b", BYTE_VALUE); - } - - @Test - void testVisitChar() { - assertEquals(CHAR_VALUE, visitor.visitChar(CHAR_VALUE, null)); - assertVisit(this.visitor, "c", CHAR_VALUE); - assertVisit(this.visitor1, "c", CHAR_VALUE); - assertVisit(this.visitor2, "c", CHAR_VALUE); - } - - @Test - void testVisitDouble() { - assertEquals(DOUBLE_VALUE, visitor.visitDouble(DOUBLE_VALUE, null)); - assertVisit(this.visitor, "d", DOUBLE_VALUE); - assertVisit(this.visitor1, "d", DOUBLE_VALUE); - assertVisit(this.visitor2, "d", DOUBLE_VALUE); - } - - @Test - void testVisitFloat() { - assertEquals(FLOAT_VALUE, visitor.visitFloat(FLOAT_VALUE, null)); - assertVisit(this.visitor, "f", FLOAT_VALUE); - assertVisit(this.visitor1, "f", FLOAT_VALUE); - assertVisit(this.visitor2, "f", FLOAT_VALUE); - } - - @Test - void testVisitInt() { - assertEquals(INT_VALUE, visitor.visitInt(INT_VALUE, null)); - assertVisit(this.visitor, "i", INT_VALUE); - assertVisit(this.visitor1, "i", INT_VALUE); - assertVisit(this.visitor2, "i", INT_VALUE); - } - - @Test - void testVisitLong() { - assertEquals(LONG_VALUE, visitor.visitLong(LONG_VALUE, null)); - assertVisit(this.visitor, "l", LONG_VALUE); - assertVisit(this.visitor1, "l", LONG_VALUE); - assertVisit(this.visitor2, "l", LONG_VALUE); - } - - @Test - void testVisitShort() { - assertEquals(SHORT_VALUE, visitor.visitShort(SHORT_VALUE, null)); - assertVisit(this.visitor, "s", SHORT_VALUE); - assertVisit(this.visitor1, "s", SHORT_VALUE); - assertVisit(this.visitor2, "s", SHORT_VALUE); - } - - @Test - void testVisitString() { - assertEquals(STRING_VALUE, visitor.visitString(STRING_VALUE, null)); - assertVisit(this.visitor, "string", STRING_VALUE); - assertVisit(this.visitor1, "string", STRING_VALUE); - assertVisit(this.visitor1, "string", STRING_VALUE); - } - - @Test - void testVisitType() { - assertEquals(TYPE_VALUE, visitor.visitType(getTypeMirror(TYPE_VALUE), null)); - assertVisit(this.visitor, "type", TYPE_VALUE); - assertVisit(this.visitor1, "type", TYPE_VALUE.getName()); - assertVisit(this.visitor2, "type", TYPE_VALUE.getName()); - } - - @Test - void testVisitEnumConstant() { - assertVisit(this.visitor, "timeUnit", HOURS); - assertVisit(this.visitor1, "timeUnit", HOURS); - assertVisit(this.visitor2, "timeUnit", HOURS); - } - - @Test - void testVisitAnnotation() { - String attributeName = "since"; - - Map attributesMap = new LinkedHashMap<>(); - attributesMap.put("module", ""); - attributesMap.put("value", "1.0.0"); - - TestAnnotation testAnnotation = testClass.getAnnotation(TestAnnotation.class); - - assertVisit(this.visitor, attributeName, testAnnotation.since()); - assertVisit(this.visitor1, attributeName, testAnnotation.since()); - assertVisit(this.visitor2, attributeName, attributesMap); - } - - @Test - void testVisitArray() { - assertVisit(this.visitor, "types", TYPES_VALUE); - assertVisit(this.visitor1, "types", of(TYPES_VALUE).map(Class::getName).toArray(String[]::new)); - assertVisit(this.visitor2, "types", of(TYPES_VALUE).map(Class::getName).toArray(String[]::new)); - } - - @Test - void testVisitUnknown() { - for (Entry elementValue : this.testAnnotationAttributes.entrySet()) { - ExecutableElement attributeMethod = elementValue.getKey(); - AnnotationValue annotationValue = elementValue.getValue(); - assertSame(annotationValue, visitor.visitUnknown(annotationValue, attributeMethod)); - } - } - - void assertVisit(AnnotationValueVisitor visitor, String attributeName, Object expectedValue) { - Entry elementValue = getElementValue(this.testAnnotationAttributes, attributeName); - ExecutableElement attributeMethod = elementValue.getKey(); - AnnotationValue annotationValue = elementValue.getValue(); - assertTrue(deepEquals(expectedValue, annotationValue.accept(visitor, attributeMethod))); - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/AnnotationUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/AnnotationUtilsTest.java deleted file mode 100644 index 7aae66dc0..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/AnnotationUtilsTest.java +++ /dev/null @@ -1,776 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.TestAnnotation; -import io.microsphere.annotation.processor.TestService; -import io.microsphere.annotation.processor.TestServiceImpl; -import io.microsphere.annotation.processor.model.Model; -import io.microsphere.annotation.processor.model.element.StringAnnotationValue; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScans; -import org.springframework.stereotype.Component; -import org.springframework.stereotype.Service; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.DeclaredType; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.xml.ws.ServiceMode; -import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Target; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import static io.microsphere.annotation.processor.util.AnnotationUtils.EMPTY_ELEMENT_TYPE_ARRAY; -import static io.microsphere.annotation.processor.util.AnnotationUtils.findAllAnnotations; -import static io.microsphere.annotation.processor.util.AnnotationUtils.findAnnotation; -import static io.microsphere.annotation.processor.util.AnnotationUtils.findAnnotations; -import static io.microsphere.annotation.processor.util.AnnotationUtils.findMetaAnnotation; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAllAnnotations; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAnnotation; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAnnotations; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAttribute; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAttributeName; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAttributesMap; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementTypes; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValue; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValues; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getValue; -import static io.microsphere.annotation.processor.util.AnnotationUtils.isAnnotationPresent; -import static io.microsphere.annotation.processor.util.AnnotationUtils.matchesAnnotationTypeName; -import static io.microsphere.annotation.processor.util.AnnotationUtils.matchesAttributeMethod; -import static io.microsphere.annotation.processor.util.AnnotationUtils.matchesAttributeValue; -import static io.microsphere.annotation.processor.util.AnnotationUtils.matchesDefaultAttributeValue; -import static io.microsphere.annotation.processor.util.FieldUtils.findField; -import static io.microsphere.annotation.processor.util.MethodUtils.findMethod; -import static io.microsphere.annotation.processor.util.MethodUtils.getAllDeclaredMethods; -import static io.microsphere.lang.function.Predicates.alwaysFalse; -import static io.microsphere.lang.function.Predicates.alwaysTrue; -import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; -import static io.microsphere.util.ArrayUtils.ofArray; -import static io.microsphere.util.StringUtils.EMPTY_STRING; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.util.Collections.emptyMap; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * The {@link AnnotationUtils} Test - * - * @author Mercy - * @since 1.0.0 - */ -class AnnotationUtilsTest extends AbstractAnnotationProcessingTest { - - @Test - void testGetAnnotation() { - asserGetAnnotation(Service.class); - } - - @Test - void testGetAnnotationWithClassName() { - asserGetAnnotation("org.springframework.stereotype.Service"); - } - - @Test - void testGetAnnotationOnNull() { - assertNull(getAnnotation(testTypeElement, NULL_CLASS)); - assertNull(getAnnotation(testTypeElement.asType(), NULL_CLASS)); - assertNull(getAnnotation(NULL_ANNOTATED_CONSTRUCT, NULL_CLASS)); - } - - @Test - void testGetAnnotationWithClassNameOnNull() { - assertNull(getAnnotation(testTypeElement, NULL_STRING)); - assertNull(getAnnotation(testTypeElement.asType(), NULL_STRING)); - assertNull(getAnnotation(NULL_ANNOTATED_CONSTRUCT, NULL_STRING)); - } - - @Test - void testGetAnnotations() { - List annotations = getAnnotations(testTypeElement); - assertEquals(4, annotations.size()); - assertAnnotation(annotations.get(0), Service.class); - assertAnnotation(annotations.get(1), ServiceMode.class); - assertAnnotation(annotations.get(2), ComponentScans.class); - assertAnnotation(annotations.get(3), TestAnnotation.class); - } - - @Test - void testGetAnnotationsOnNull() { - List annotations = getAnnotations(NULL_ANNOTATED_CONSTRUCT); - assertEmptyList(annotations); - } - - @Test - void testGetAnnotationsWithAnnotationClass() { - assertGetAnnotations(Service.class); - assertGetAnnotations(ServiceMode.class); - } - - @Test - void testGetAnnotationsWithAnnotationClassOnNull() { - assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, NULL_CLASS).isEmpty()); - assertTrue(getAnnotations(testTypeElement, NULL_CLASS).isEmpty()); - assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, Service.class).isEmpty()); - } - - @Test - void testGetAnnotationsWithAnnotationClassOnNotFound() { - List annotations = getAnnotations(testTypeElement, Override.class); - assertEquals(0, annotations.size()); - } - - @Test - void testGetAnnotationsWithAnnotationClassName() { - assertGetAnnotations("org.springframework.stereotype.Service"); - assertGetAnnotations("javax.xml.ws.ServiceMode"); - } - - @Test - void testGetAnnotationsWithAnnotationClassNameOnNull() { - assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, NULL_STRING).isEmpty()); - assertTrue(getAnnotations(testTypeElement, NULL_STRING).isEmpty()); - assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, "org.springframework.stereotype.Service").isEmpty()); - } - - @Test - void testGetAllAnnotations() { - List annotations = getAllAnnotations(testTypeElement); - assertEquals(5, annotations.size()); - - annotations = getAllAnnotations(testTypeMirror); - assertEquals(5, annotations.size()); - } - - @Test - void testGetAllAnnotationsOnNull() { - assertEmptyList(getAllAnnotations(NULL_ELEMENT)); - assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR)); - } - - @Test - void testGetAllAnnotationsWithAnnotationClass() { - List annotations = getAllAnnotations(testTypeElement, Override.class); - assertEquals(0, annotations.size()); - - annotations = getAllAnnotations(testTypeMirror, Override.class); - assertEquals(0, annotations.size()); - - annotations = getAllAnnotations(testTypeElement, Service.class); - assertEquals(1, annotations.size()); - - annotations = getAllAnnotations(testTypeMirror, Service.class); - assertEquals(1, annotations.size()); - - annotations = getAllAnnotations(processingEnv, TestServiceImpl.class); - assertEquals(5, annotations.size()); - } - - @Test - void testGetAllAnnotationsWithAnnotationClassOnNull() { - assertEmptyList(getAllAnnotations(NULL_ELEMENT, NULL_CLASS)); - assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR, NULL_CLASS)); - assertEmptyList(getAllAnnotations(NULL_PROCESSING_ENVIRONMENT, NULL_CLASS)); - - assertEmptyList(getAllAnnotations(NULL_ELEMENT, Service.class)); - assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR, Service.class)); - assertEmptyList(getAllAnnotations(NULL_PROCESSING_ENVIRONMENT, Service.class)); - - assertEmptyList(getAllAnnotations(testTypeElement, NULL_CLASS)); - assertEmptyList(getAllAnnotations(testTypeMirror, NULL_CLASS)); - assertEmptyList(getAllAnnotations(processingEnv, NULL_CLASS)); - } - - @Test - void testGetAllAnnotationsWithAnnotationClassName() { - List annotations = getAllAnnotations(testTypeElement, "java.lang.Override"); - assertEquals(0, annotations.size()); - - annotations = getAllAnnotations(testTypeMirror, "org.springframework.stereotype.Service"); - assertEquals(1, annotations.size()); - } - - @Test - void testGetAllAnnotationsWithAnnotationClassNameOnNull() { - assertEmptyList(getAllAnnotations(NULL_ELEMENT, NULL_STRING)); - assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR, NULL_STRING)); - - assertTrue(getAllAnnotations(NULL_ELEMENT, "org.springframework.stereotype.Service").isEmpty()); - assertTrue(getAllAnnotations(NULL_TYPE_MIRROR, "org.springframework.stereotype.Service").isEmpty()); - - assertEmptyList(getAllAnnotations(testTypeElement, NULL_STRING)); - assertEmptyList(getAllAnnotations(testTypeMirror, NULL_STRING)); - } - - @Test - void testFindAnnotation() { - assertFindAnnotation(Service.class); - assertFindAnnotation(Path.class); - } - - @Test - void testFindAnnotationOnNotFound() { - assertNull(findAnnotation(testTypeMirror, Target.class)); - assertNull(findAnnotation(testTypeElement, Target.class)); - assertNull(findAnnotation(testTypeMirror, Override.class)); - assertNull(findAnnotation(testTypeElement, Override.class)); - } - - @Test - void testFindAnnotationOnNull() { - assertNull(findAnnotation(NULL_ELEMENT, NULL_CLASS)); - assertNull(findAnnotation(NULL_TYPE_MIRROR, NULL_CLASS)); - assertNull(findAnnotation(testTypeMirror, NULL_CLASS)); - assertNull(findAnnotation(testTypeElement, NULL_CLASS)); - - assertNull(findAnnotation(NULL_ELEMENT, NULL_STRING)); - assertNull(findAnnotation(NULL_TYPE_MIRROR, NULL_STRING)); - assertNull(findAnnotation(testTypeMirror, NULL_STRING)); - assertNull(findAnnotation(testTypeElement, NULL_STRING)); - } - - @Test - void testFindMetaAnnotationWithAnnotationClass() { - getAllDeclaredMethods(getTypeElement(TestService.class)).forEach(method -> { - assertFindMetaAnnotation(method, HttpMethod.class); - }); - } - - @Test - void testFindMetaAnnotationWithAnnotationClassOnNotFound() { - assertNull(findMetaAnnotation(testTypeElement, Service.class)); - } - - @Test - void testFindMetaAnnotationWithAnnotationClassNameOnNotFound() { - assertNull(findMetaAnnotation(testTypeElement, "org.springframework.stereotype.Service")); - } - - @Test - void testFindMetaAnnotationWithAnnotationClassOnNull() { - assertNull(findMetaAnnotation(NULL_ELEMENT, NULL_CLASS)); - assertNull(findMetaAnnotation(NULL_ELEMENT, Service.class)); - assertNull(findMetaAnnotation(testTypeElement, NULL_CLASS)); - } - - @Test - void testFindMetaAnnotationWithAnnotationClassName() { - getAllDeclaredMethods(getTypeElement(TestService.class)).forEach(method -> { - assertFindMetaAnnotation(method, "javax.ws.rs.HttpMethod"); - }); - } - - @Test - void testFindMetaAnnotationWithAnnotationClassNameOnNull() { - assertNull(findMetaAnnotation(NULL_ELEMENT, NULL_STRING)); - assertNull(findMetaAnnotation(NULL_ELEMENT, "test")); - assertNull(findMetaAnnotation(testTypeElement, NULL_STRING)); - } - - @Test - void testFindAllAnnotationsWithTypeMirror() { - List annotations = findAllAnnotations(testTypeMirror, alwaysTrue()); - assertEquals(5, annotations.size()); - - annotations = findAllAnnotations(testTypeMirror, alwaysFalse()); - assertEmptyList(annotations); - } - - @Test - void testFindAllAnnotationsWithTypeElement() { - List annotations = findAllAnnotations(testTypeElement, alwaysTrue()); - assertEquals(5, annotations.size()); - - annotations = findAllAnnotations(testTypeElement, alwaysFalse()); - assertEmptyList(annotations); - } - - @Test - void testFindAllAnnotationsWithMethod() { - ExecutableElement method = findMethod(testTypeElement, "echo", String.class); - - List annotations = findAllAnnotations(method, alwaysTrue()); - assertEquals(1, annotations.size()); - assertAnnotation(annotations.get(0), Cacheable.class); - - method = findMethod(getTypeElement(TestService.class), "echo", String.class); - - annotations = findAllAnnotations(method); - assertEquals(1, annotations.size()); - assertAnnotation(annotations.get(0), GET.class); - } - - @Test - void testFindAllAnnotationsWithMethodParameters() { - ExecutableElement method = findMethod(getTypeElement(TestService.class), "echo", String.class); - List parameters = method.getParameters(); - assertEquals(1, parameters.size()); - - List annotations = findAllAnnotations(parameters.get(0), alwaysTrue()); - assertEquals(2, annotations.size()); - assertAnnotation(annotations.get(0), PathParam.class); - assertAnnotation(annotations.get(1), DefaultValue.class); - - method = findMethod(getTypeElement(TestService.class), "model", Model.class); - parameters = method.getParameters(); - assertEquals(1, parameters.size()); - - annotations = findAllAnnotations(parameters.get(0)); - assertEquals(1, annotations.size()); - assertAnnotation(annotations.get(0), PathParam.class); - } - - @Test - void testFindAllAnnotationsWithField() { - VariableElement field = findField(testTypeElement, "context"); - - List annotations = findAllAnnotations(field, alwaysTrue()); - assertEquals(1, annotations.size()); - assertAnnotation(annotations.get(0), Autowired.class); - - field = findField(testTypeElement, "environment"); - annotations = findAllAnnotations(field, alwaysTrue()); - assertEmptyList(annotations); - } - - @Test - void testFindAllAnnotationsWithTypeMirrorOnNull() { - assertEmptyList(findAllAnnotations(NULL_TYPE_MIRROR, alwaysTrue())); - assertEmptyList(findAllAnnotations(NULL_TYPE_MIRROR, alwaysFalse())); - } - - @Test - void testFindAllAnnotationsWithTypeElementOnNull() { - assertEmptyList(findAllAnnotations(NULL_TYPE_ELEMENT, alwaysTrue())); - assertEmptyList(findAllAnnotations(NULL_TYPE_ELEMENT, alwaysFalse())); - } - - @Test - void testFindAllAnnotationsWithElementOnNull() { - assertEmptyList(findAllAnnotations(NULL_ELEMENT, alwaysTrue())); - assertEmptyList(findAllAnnotations(NULL_ELEMENT, alwaysFalse())); - } - - @Test - void testFindAllAnnotationsOnNull() { - assertEmptyList(findAllAnnotations(NULL_PROCESSING_ENVIRONMENT, Service.class, alwaysTrue())); - assertEmptyList(findAllAnnotations(NULL_PROCESSING_ENVIRONMENT, Service.class, alwaysTrue())); - assertEmptyList(findAllAnnotations(NULL_PROCESSING_ENVIRONMENT, "org.springframework.stereotype.Service", alwaysFalse())); - assertEmptyList(findAllAnnotations(NULL_PROCESSING_ENVIRONMENT, "org.springframework.stereotype.Service", alwaysFalse())); - assertEmptyList(findAllAnnotations(processingEnv, NULL_TYPE, alwaysTrue())); - assertEmptyList(findAllAnnotations(processingEnv, NULL_TYPE, alwaysFalse())); - assertEmptyList(findAllAnnotations(processingEnv, NULL_STRING, alwaysTrue())); - assertEmptyList(findAllAnnotations(processingEnv, NULL_STRING, alwaysFalse())); - } - - @Test - void testMatchesAnnotationClass() { - AnnotationMirror annotation = findAnnotation(testTypeElement, Service.class); - assertTrue(AnnotationUtils.matchesAnnotationType(annotation, Service.class)); - } - - @Test - void testMatchesAnnotationClassOnNull() { - assertFalse(AnnotationUtils.matchesAnnotationType(NULL_ANNOTATION_MIRROR, Service.class)); - assertFalse(AnnotationUtils.matchesAnnotationType(findAnnotation(testTypeElement, Service.class), NULL_CLASS)); - } - - @Test - void testMatchesAnnotationTypeName() { - AnnotationMirror annotation = findAnnotation(testTypeElement, "org.springframework.stereotype.Service"); - assertTrue(matchesAnnotationTypeName(annotation, "org.springframework.stereotype.Service")); - } - - @Test - void testMatchesAnnotationTypeNameOnNull() { - assertFalse(matchesAnnotationTypeName(NULL_ANNOTATION_MIRROR, "org.springframework.stereotype.Service")); - assertFalse(matchesAnnotationTypeName(findAnnotation(testTypeElement, "org.springframework.stereotype.Service"), NULL_STRING)); - } - - @Test - void testGetAttribute() { - assertEquals("testService", getAttribute(findAnnotation(testTypeElement, Service.class), "value")); - assertEquals("testService", getAttribute(findAnnotation(testTypeElement, Service.class), "value", false)); - assertEquals("/echo", getAttribute(findAnnotation(testTypeElement, Path.class), "value")); - - assertNull(getAttribute(findAnnotation(testTypeElement, Path.class), NULL_STRING)); - assertNull(getAttribute(findAnnotation(testTypeElement, NULL_CLASS), NULL_STRING)); - - ExecutableElement echoMethod = findMethod(testTypeElement, "echo", String.class); - AnnotationMirror cacheableAnnotation = findAnnotation(echoMethod, Cacheable.class); - String[] cacheNames = getAttribute(cacheableAnnotation, "cacheNames"); - assertArrayEquals(ofArray("cache-1", "cache-2"), cacheNames); - - String key = getAttribute(cacheableAnnotation, "key"); - assertEquals(EMPTY_STRING, key); - - DeclaredType cacheableAnnotationType = cacheableAnnotation.getAnnotationType(); - AnnotationMirror targetAnnotation = findAnnotation(cacheableAnnotationType, Target.class); - ElementType[] elementTypes = getAttribute(targetAnnotation, "value"); - assertArrayEquals(ofArray(TYPE, METHOD), elementTypes); - - } - - @Test - void testGetValue() { - AnnotationMirror pathAnnotation = getAnnotation(getTypeElement(TestService.class), Path.class); - assertEquals("/echo", getValue(pathAnnotation)); - } - - @Test - void testIsAnnotationPresentOnAnnotationClass() { - assertTrue(isAnnotationPresent(testTypeElement, Service.class)); - assertTrue(isAnnotationPresent(testTypeElement, Component.class)); - assertTrue(isAnnotationPresent(testTypeElement, ServiceMode.class)); - assertTrue(isAnnotationPresent(testTypeElement, Inherited.class)); - assertTrue(isAnnotationPresent(testTypeElement, Documented.class)); - } - - @Test - void testIsAnnotationPresentOnAnnotationClassOnNull() { - assertFalse(isAnnotationPresent(NULL_ELEMENT, Service.class)); - assertFalse(isAnnotationPresent(testTypeElement, NULL_CLASS)); - assertFalse(isAnnotationPresent(testTypeElement, Override.class)); - } - - @Test - void testIsAnnotationPresentOnAnnotationClassName() { - assertTrue(isAnnotationPresent(testTypeElement, "org.springframework.stereotype.Service")); - assertTrue(isAnnotationPresent(testTypeElement, "org.springframework.stereotype.Component")); - assertTrue(isAnnotationPresent(testTypeElement, "javax.xml.ws.ServiceMode")); - assertTrue(isAnnotationPresent(testTypeElement, "java.lang.annotation.Inherited")); - assertTrue(isAnnotationPresent(testTypeElement, "java.lang.annotation.Documented")); - } - - @Test - void testIsAnnotationPresentOnAnnotationClassNameOnNull() { - assertFalse(isAnnotationPresent(NULL_ELEMENT, "org.springframework.stereotype.Service")); - assertFalse(isAnnotationPresent(testTypeElement, NULL_STRING)); - assertFalse(isAnnotationPresent(testTypeElement, "java.lang.Override")); - - } - - @Test - void testFindAnnotations() { - List annotations = findAnnotations(testTypeElement); - assertEquals(4, annotations.size()); - assertAnnotation(annotations.get(0), Service.class); - assertAnnotation(annotations.get(1), ServiceMode.class); - assertAnnotation(annotations.get(2), ComponentScans.class); - assertAnnotation(annotations.get(3), TestAnnotation.class); - - annotations = findAnnotations(testTypeElement, alwaysTrue()); - assertEquals(4, annotations.size()); - assertAnnotation(annotations.get(0), Service.class); - assertAnnotation(annotations.get(1), ServiceMode.class); - assertAnnotation(annotations.get(2), ComponentScans.class); - assertAnnotation(annotations.get(3), TestAnnotation.class); - - annotations = findAnnotations(testTypeElement, alwaysFalse()); - assertEmptyList(annotations); - } - - @Test - void testFindAnnotationsOnNotFound() { - assertEmptyList(findAnnotations(getTypeElement(Serializable.class))); - } - - @Test - void testFindAnnotationsOnNull() { - assertEmptyList(findAnnotations(NULL_ELEMENT)); - } - - @Test - void testGetAttributeName() { - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); - for (Entry entry : elementValues.entrySet()) { - ExecutableElement attributeMethod = entry.getKey(); - assertEquals(attributeMethod.getSimpleName().toString(), getAttributeName(attributeMethod)); - } - } - - @Test - void testMatchesAttributeMethod() { - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); - for (Entry entry : elementValues.entrySet()) { - ExecutableElement attributeMethod = entry.getKey(); - assertTrue(matchesAttributeMethod(attributeMethod, getAttributeName(attributeMethod))); - } - } - - @Test - void testMatchesAttributeMethodOnNull() { - assertFalse(matchesAttributeMethod(null, null)); - - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); - for (Entry entry : elementValues.entrySet()) { - ExecutableElement attributeMethod = entry.getKey(); - assertFalse(matchesAttributeMethod(attributeMethod, null)); - } - } - - @Test - void testMatchesAttributeValue() { - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); - for (Entry entry : elementValues.entrySet()) { - AnnotationValue annotationValue = entry.getValue(); - assertTrue(matchesAttributeValue(annotationValue, annotationValue)); - assertTrue(matchesAttributeValue(annotationValue, annotationValue.getValue())); - } - - assertTrue(matchesAttributeValue(new StringAnnotationValue(""), new StringAnnotationValue(""))); - } - - @Test - void testMatchesAttributeValueOnNull() { - assertTrue(matchesAttributeValue(null, null)); - assertFalse(matchesAttributeValue(null, (Object) null)); - - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); - for (Entry entry : elementValues.entrySet()) { - AnnotationValue annotationValue = entry.getValue(); - assertFalse(matchesAttributeValue(annotationValue, null)); - assertFalse(matchesAttributeValue(annotationValue, (Object) null)); - } - } - - @Test - void testMatchesDefaultAttributeValue() { - Map elementValues = getElementValues(testTypeElement, ServiceMode.class); - for (Entry entry : elementValues.entrySet()) { - ExecutableElement attributeMethod = entry.getKey(); - assertTrue(matchesDefaultAttributeValue(attributeMethod, attributeMethod.getDefaultValue())); - } - } - - @Test - void testGetElementValue() { - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); - for (Entry entry : elementValues.entrySet()) { - ExecutableElement attributeMethod = entry.getKey(); - String attributeName = getAttributeName(attributeMethod); - assertEquals(entry, getElementValue(elementValues, attributeName)); - } - - assertNull(getElementValue(elementValues, "unknown")); - } - - @Test - void testGetElementValueOnEmptyElementValues() { - AnnotationMirror annotation = findAnnotation(testTypeElement, ServiceMode.class); - Map elementValues = annotation.getElementValues(); - assertNull(getElementValue(elementValues, "value")); - } - - @Test - void testGetElementValueOnNull() { - assertNull(getElementValue(null, "value")); - } - - @Test - void testGetElementValuesMapOnAnnotatedClass() { - Map attributesMap = getAttributesMap(testTypeElement, Service.class); - assertEquals(1, attributesMap.size()); - assertEquals("testService", attributesMap.get("value")); - } - - @Test - void testGetElementValuesMapOnAnnotatedMethod() { - ExecutableElement method = findMethod(testTypeElement, "echo", String.class); - Map attributesMap = getAttributesMap(method, Cacheable.class); - assertEquals(9, attributesMap.size()); - assertArrayEquals(ofArray("cache-1", "cache-2"), (String[]) attributesMap.get("cacheNames")); - } - - @Test - void testGetElementValuesMapOnRepeatableAnnotation() { - Map attributesMap = getAttributesMap(testTypeElement, ComponentScans.class); - assertEquals(1, attributesMap.size()); - - ComponentScans componentScans = testClass.getAnnotation(ComponentScans.class); - ComponentScan[] componentScanArray = (ComponentScan[]) attributesMap.get("value"); - assertEquals(2, componentScanArray.length); - assertArrayEquals(componentScanArray, componentScans.value()); - } - - @Test - void testGetElementValuesMapOnNull() { - Map attributesMap = getAttributesMap(null, null); - assertSame(emptyMap(), attributesMap); - - attributesMap = getAttributesMap(testTypeElement, null); - assertSame(emptyMap(), attributesMap); - - attributesMap = getAttributesMap(null); - assertSame(emptyMap(), attributesMap); - } - - @Test - void testGetElementValuesOnAnnotatedClass() { - Map elementValues = getElementValues(testTypeElement, Service.class); - assertServiceAttributes(elementValues); - - elementValues = getElementValues(testTypeElement, Service.class, false); - assertServiceAttributes(elementValues); - } - - @Test - void testGetElementValuesOnAnnotatedMethod() { - ExecutableElement method = findMethod(testTypeElement, "echo", String.class); - Map elementValues = getElementValues(method, Cacheable.class, false); - assertEquals(1, elementValues.size()); - assertAttributeEntry(elementValues, "cacheNames", ofArray("cache-1", "cache-2")); - - - elementValues = getElementValues(method, Cacheable.class, true); - assertEquals(9, elementValues.size()); - assertAttributeEntry(elementValues, "value", EMPTY_STRING_ARRAY); - assertAttributeEntry(elementValues, "cacheNames", ofArray("cache-1", "cache-2")); - assertAttributeEntry(elementValues, "key", EMPTY_STRING); - assertAttributeEntry(elementValues, "keyGenerator", EMPTY_STRING); - assertAttributeEntry(elementValues, "cacheManager", EMPTY_STRING); - assertAttributeEntry(elementValues, "cacheResolver", EMPTY_STRING); - assertAttributeEntry(elementValues, "condition", EMPTY_STRING); - assertAttributeEntry(elementValues, "unless", EMPTY_STRING); - assertAttributeEntry(elementValues, "sync", false); - } - - @Test - void testGetElementValuesOnNull() { - Map elementValues = getElementValues(null); - assertSame(emptyMap(), elementValues); - } - - @Test - void testGetElementTypes() { - assertElementTypes(Service.class, TYPE); - assertElementTypes(ServiceMode.class, TYPE); - assertElementTypes(ComponentScans.class, TYPE); - assertElementTypes(TestAnnotation.class, TYPE); - } - - void assertElementTypes(Class annotationClass, ElementType... expectedElementTypes) { - AnnotationMirror annotationMirror = findAnnotation(this.testTypeElement, annotationClass); - assertArrayEquals(expectedElementTypes, getElementTypes(annotationMirror)); - } - - @Test - void testGetElementTypesOnNull() { - assertSame(EMPTY_ELEMENT_TYPE_ARRAY, getElementTypes((AnnotationMirror) null)); - assertSame(EMPTY_ELEMENT_TYPE_ARRAY, getElementTypes((DeclaredType) null)); - } - - void assertServiceAttributes(Map attributes) { - assertEquals(1, attributes.size()); - assertAttributeEntry(attributes, "value", "testService"); - } - - void assertAttributeEntry(Map attributes, String attributeName, Object attributeValue) { - for (Entry entry : attributes.entrySet()) { - ExecutableElement attributeMethod = entry.getKey(); - if (matchesAttributeMethod(attributeMethod, attributeName)) { - assertAttributeEntry(entry, attributeName, attributeValue); - break; - } - } - } - - void assertAttributeEntry(Entry attributeEntry, String attributeName, Object attributeValue) { - ExecutableElement attributeMethod = attributeEntry.getKey(); - AnnotationValue annotationValue = attributeEntry.getValue(); - assertEquals(attributeName, getAttributeName(attributeMethod)); - Object value = getAttribute(attributeEntry); - Class attributeValueClass = value.getClass(); - if (attributeValueClass.isArray()) { - Class componentType = attributeValueClass.getComponentType(); - if (String.class.equals(componentType)) { - assertArrayEquals((String[]) attributeValue, (String[]) value); - } - } else { - assertEquals(attributeValue, value); - } - } - - private void assertFindMetaAnnotation(Element element, Class annotationClass) { - assertAnnotation(findMetaAnnotation(element, annotationClass), annotationClass); - } - - private void assertFindMetaAnnotation(Element element, String annotationClassName) { - assertAnnotation(findMetaAnnotation(element, annotationClassName), annotationClassName); - } - - private void assertFindAnnotation(Class annotationClass) { - assertAnnotation(findAnnotation(testTypeMirror, annotationClass), annotationClass); - assertAnnotation(findAnnotation(testTypeElement, annotationClass), annotationClass); - assertAnnotation(findAnnotation(testTypeMirror, annotationClass.getName()), annotationClass); - assertAnnotation(findAnnotation(testTypeElement, annotationClass.getName()), annotationClass); - } - - private void asserGetAnnotation(Class annotationClass) { - AnnotationMirror annotation = getAnnotation(testTypeElement, annotationClass); - assertAnnotation(annotation, annotationClass); - } - - private void asserGetAnnotation(String annotationClassName) { - AnnotationMirror annotation = getAnnotation(testTypeElement, annotationClassName); - assertAnnotation(annotation, annotationClassName); - } - - private void assertGetAnnotations(Class annotationClass) { - List annotations = getAnnotations(testTypeElement, annotationClass); - assertEquals(1, annotations.size()); - assertAnnotation(annotations.get(0), annotationClass); - } - - private void assertGetAnnotations(String annotationClassName) { - List annotations = getAnnotations(testTypeElement, annotationClassName); - assertEquals(1, annotations.size()); - assertAnnotation(annotations.get(0), annotationClassName); - } - - private void assertAnnotation(AnnotationMirror annotation, Class annotationClass) { - assertTrue(AnnotationUtils.matchesAnnotationType(annotation, annotationClass)); - assertAnnotation(annotation, annotationClass.getName()); - } - - private void assertAnnotation(AnnotationMirror annotation, String annotationClassName) { - assertEquals(annotation.getAnnotationType().toString(), annotationClassName); - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ClassUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ClassUtilsTest.java deleted file mode 100644 index 3785041ad..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ClassUtilsTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.ComponentScan; - -import static io.microsphere.annotation.processor.util.ClassUtils.getClassName; -import static io.microsphere.annotation.processor.util.ClassUtils.loadClass; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; - -/** - * {@link ClassUtils} Test - * - * @author Mercy - * @see ClassUtils - * @since 1.0.0 - */ -class ClassUtilsTest extends AbstractAnnotationProcessingTest { - - @Test - void testGetClassName() { - assertEquals(this.testClassName, getClassName(this.testTypeMirror)); - } - - @Test - void testLoadClassOnTypeMirror() { - assertSame(this.testClass, loadClass(this.testTypeMirror)); - } - - @Test - void testLoadClass() { - assertSame(ComponentScan.Filter.class, loadClass("org.springframework.context.annotation.ComponentScan.Filter")); - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ConstructorUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ConstructorUtilsTest.java deleted file mode 100644 index 09aac37d9..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ConstructorUtilsTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.util; - - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import org.junit.jupiter.api.Test; -import org.springframework.core.env.Environment; - -import javax.lang.model.element.ExecutableElement; -import java.io.Serializable; -import java.util.List; - -import static io.microsphere.annotation.processor.util.ConstructorUtils.findConstructor; -import static io.microsphere.annotation.processor.util.ConstructorUtils.findDeclaredConstructors; -import static io.microsphere.annotation.processor.util.ConstructorUtils.getDeclaredConstructors; -import static io.microsphere.annotation.processor.util.ElementUtils.matchParameterTypes; -import static io.microsphere.lang.function.Predicates.alwaysFalse; -import static io.microsphere.lang.function.Predicates.alwaysTrue; -import static java.util.Collections.emptyList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * {@link ConstructorUtils} Test - * - * @author Mercy - * @see ConstructorUtils - * @since 1.0.0 - */ -class ConstructorUtilsTest extends AbstractAnnotationProcessingTest { - - @Test - void testGetDeclaredConstructors() { - List constructors = getDeclaredConstructors(this.testTypeElement); - assertTestServiceImplConstructors(constructors); - - constructors = getDeclaredConstructors(this.testDeclaredType); - assertTestServiceImplConstructors(constructors); - } - - @Test - void testGetDeclaredConstructorsOnNull() { - assertSame(emptyList(), getDeclaredConstructors(NULL_TYPE_ELEMENT)); - assertSame(emptyList(), getDeclaredConstructors(NULL_TYPE_MIRROR)); - } - - @Test - void testFindConstructor() { - assertTestServiceImpl1stConstructor(ConstructorUtils.findConstructor(this.testTypeElement)); - assertTestServiceImpl1stConstructor(findConstructor(this.testDeclaredType)); - - assertTestServiceImpl2ndConstructor(ConstructorUtils.findConstructor(this.testTypeElement, Environment.class)); - assertTestServiceImpl2ndConstructor(findConstructor(this.testDeclaredType, Environment.class)); - } - - @Test - void testFindConstructorOnNull() { - assertNull(ConstructorUtils.findConstructor(NULL_TYPE_ELEMENT)); - assertNull(findConstructor(NULL_TYPE_MIRROR)); - - assertNull(ConstructorUtils.findConstructor(this.testTypeElement, null)); - assertNull(findConstructor(this.testDeclaredType, null)); - } - - @Test - void testFindConstructorOnMismatch() { - assertNull(ConstructorUtils.findConstructor(NULL_TYPE_ELEMENT, Object.class)); - assertNull(findConstructor(NULL_TYPE_MIRROR, Object.class)); - - assertNull(ConstructorUtils.findConstructor(NULL_TYPE_ELEMENT, Object.class, String.class)); - assertNull(findConstructor(NULL_TYPE_MIRROR, Object.class, String.class)); - - assertNull(ConstructorUtils.findConstructor(NULL_TYPE_ELEMENT, Object.class, String.class, Integer.class)); - assertNull(findConstructor(NULL_TYPE_MIRROR, Object.class, String.class, Integer.class)); - } - - @Test - void testFindConstructors() { - List constructors = findDeclaredConstructors(this.testTypeElement); - assertTestServiceImplConstructors(constructors); - - constructors = findDeclaredConstructors(this.testDeclaredType); - assertTestServiceImplConstructors(constructors); - - constructors = findDeclaredConstructors(this.testTypeElement, alwaysTrue()); - assertTestServiceImplConstructors(constructors); - - constructors = findDeclaredConstructors(this.testDeclaredType, alwaysTrue()); - assertTestServiceImplConstructors(constructors); - } - - @Test - void testFindConstructorsOnNull() { - assertSame(emptyList(), findDeclaredConstructors(NULL_TYPE_ELEMENT)); - assertSame(emptyList(), findDeclaredConstructors(NULL_TYPE_MIRROR)); - - assertSame(emptyList(), findDeclaredConstructors(this.testTypeElement, null)); - assertSame(emptyList(), findDeclaredConstructors(this.testDeclaredType, null)); - } - - @Test - void testFindConstructorsOnMismatch() { - assertSame(emptyList(), findDeclaredConstructors(this.testTypeElement, alwaysFalse())); - assertSame(emptyList(), findDeclaredConstructors(this.testDeclaredType, alwaysFalse())); - } - - @Test - void testFindConstructorsOnNotFound() { - assertSame(emptyList(), findDeclaredConstructors(getTypeElement(Serializable.class))); - assertSame(emptyList(), findDeclaredConstructors(getDeclaredType(Serializable.class))); - - assertSame(emptyList(), findDeclaredConstructors(getTypeElement(List.class))); - assertSame(emptyList(), findDeclaredConstructors(getDeclaredType(List.class))); - } - - void assertTestServiceImplConstructors(List constructors) { - assertEquals(2, constructors.size()); - assertTestServiceImpl1stConstructor(constructors.get(0)); - assertTestServiceImpl2ndConstructor(constructors.get(1)); - } - - void assertTestServiceImpl1stConstructor(ExecutableElement constructor) { - assertEquals(this.testTypeElement, constructor.getEnclosingElement()); - assertEquals(emptyList(), constructor.getParameters()); - } - - void assertTestServiceImpl2ndConstructor(ExecutableElement constructor) { - assertEquals(this.testTypeElement, constructor.getEnclosingElement()); - assertEquals(1, constructor.getParameters().size()); - assertTrue(matchParameterTypes(constructor, Environment.class)); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ElementUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ElementUtilsTest.java deleted file mode 100644 index 91cb83a6f..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ElementUtilsTest.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.util; - - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; -import java.lang.annotation.ElementType; -import java.util.List; - -import static io.microsphere.annotation.processor.util.ElementUtils.filterElements; -import static io.microsphere.annotation.processor.util.ElementUtils.hasModifiers; -import static io.microsphere.annotation.processor.util.ElementUtils.isClass; -import static io.microsphere.annotation.processor.util.ElementUtils.isDeclaredType; -import static io.microsphere.annotation.processor.util.ElementUtils.isExecutable; -import static io.microsphere.annotation.processor.util.ElementUtils.isField; -import static io.microsphere.annotation.processor.util.ElementUtils.isInitializer; -import static io.microsphere.annotation.processor.util.ElementUtils.isInterface; -import static io.microsphere.annotation.processor.util.ElementUtils.isMember; -import static io.microsphere.annotation.processor.util.ElementUtils.isPublicNonStatic; -import static io.microsphere.annotation.processor.util.ElementUtils.isVariable; -import static io.microsphere.annotation.processor.util.ElementUtils.matchParameterTypeNames; -import static io.microsphere.annotation.processor.util.ElementUtils.matchParameterTypes; -import static io.microsphere.annotation.processor.util.ElementUtils.matchesElementKind; -import static io.microsphere.annotation.processor.util.ElementUtils.matchesElementType; -import static io.microsphere.annotation.processor.util.ElementUtils.toElementKind; -import static io.microsphere.annotation.processor.util.MemberUtils.getAllDeclaredMembers; -import static io.microsphere.annotation.processor.util.MemberUtils.getDeclaredMembers; -import static io.microsphere.annotation.processor.util.MethodUtils.findMethod; -import static io.microsphere.collection.ListUtils.ofList; -import static io.microsphere.lang.function.Predicates.alwaysFalse; -import static io.microsphere.lang.function.Predicates.alwaysTrue; -import static java.lang.annotation.ElementType.PACKAGE; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.ElementType.TYPE_USE; -import static java.lang.annotation.ElementType.values; -import static java.util.Collections.emptyList; -import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE; -import static javax.lang.model.element.ElementKind.CLASS; -import static javax.lang.model.element.ElementKind.CONSTRUCTOR; -import static javax.lang.model.element.ElementKind.ENUM; -import static javax.lang.model.element.ElementKind.ENUM_CONSTANT; -import static javax.lang.model.element.ElementKind.EXCEPTION_PARAMETER; -import static javax.lang.model.element.ElementKind.FIELD; -import static javax.lang.model.element.ElementKind.INSTANCE_INIT; -import static javax.lang.model.element.ElementKind.INTERFACE; -import static javax.lang.model.element.ElementKind.LOCAL_VARIABLE; -import static javax.lang.model.element.ElementKind.METHOD; -import static javax.lang.model.element.ElementKind.OTHER; -import static javax.lang.model.element.ElementKind.PARAMETER; -import static javax.lang.model.element.ElementKind.RESOURCE_VARIABLE; -import static javax.lang.model.element.ElementKind.STATIC_INIT; -import static javax.lang.model.element.Modifier.PRIVATE; -import static javax.lang.model.util.ElementFilter.fieldsIn; -import static javax.lang.model.util.ElementFilter.methodsIn; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * {@link ElementUtils} Test - * - * @author Mercy - * @see ElementUtils - * @since 1.0.0 - */ -class ElementUtilsTest extends AbstractAnnotationProcessingTest { - - private ExecutableElement echoMethod; - - @Override - protected void beforeTest() { - super.beforeTest(); - this.echoMethod = findMethod(testTypeElement, "echo", "java.lang.String"); - } - - @Test - void testMatchesElementTypeElementKind() { - assertTrue(matchesElementKind(echoMethod, METHOD)); - assertFalse(matchesElementKind(echoMethod, FIELD)); - } - - @Test - void testMatchesElementTypeElementKindOnNull() { - assertFalse(matchesElementKind(NULL_ELEMENT, FIELD)); - assertFalse(matchesElementKind(echoMethod, NULL_ELEMENT_KIND)); - } - - @Test - void testIsPublicNonStatic() { - methodsIn(getDeclaredMembers(testTypeElement)).forEach(method -> assertTrue(isPublicNonStatic(method))); - - // Integer#valueOf(String) is a public static method - assertFalse(isPublicNonStatic(findMethod(getTypeElement(Integer.class), "valueOf", String.class))); - } - - @Test - void testIsPublicNonStaticOnNull() { - assertFalse(isPublicNonStatic(NULL_ELEMENT)); - } - - @Test - void testHasModifiers() { - List members = getAllDeclaredMembers(testTypeElement.asType()); - List fields = fieldsIn(members); - assertTrue(hasModifiers(fields.get(0), PRIVATE)); - } - - @Test - void testHasModifiersOnNull() { - assertFalse(hasModifiers(NULL_ELEMENT)); - assertFalse(hasModifiers(testTypeElement, null)); - } - - @Test - void testIsClass() { - assertTrue(isClass(CLASS)); - assertTrue(isClass(ENUM)); - assertFalse(isClass(INTERFACE)); - } - - @Test - void testIsClassOnNull() { - assertFalse(isClass(null)); - } - - @Test - void testIsInterface() { - assertTrue(isInterface(INTERFACE)); - assertTrue(isInterface(ANNOTATION_TYPE)); - assertFalse(isInterface(CLASS)); - } - - @Test - void testIsInterfaceOnNull() { - assertFalse(isInterface(null)); - } - - @Test - void testIsDeclaredType() { - assertTrue(isDeclaredType(CLASS)); - assertTrue(isDeclaredType(ENUM)); - assertTrue(isDeclaredType(INTERFACE)); - assertTrue(isDeclaredType(ANNOTATION_TYPE)); - assertFalse(isDeclaredType(LOCAL_VARIABLE)); - } - - @Test - void testIsDeclaredTypeOnNull() { - assertFalse(isDeclaredType(null)); - } - - @Test - void testIsField() { - assertTrue(isField(FIELD)); - assertTrue(isField(ENUM_CONSTANT)); - assertFalse(isField(LOCAL_VARIABLE)); - } - - @Test - void testIsFieldOnNull() { - assertFalse(isField(null)); - } - - @Test - void testIsExecutable() { - assertTrue(isExecutable(METHOD)); - assertTrue(isExecutable(CONSTRUCTOR)); - assertTrue(isExecutable(STATIC_INIT)); - assertTrue(isExecutable(INSTANCE_INIT)); - assertFalse(isExecutable(CLASS)); - } - - @Test - void testIsExecutableOnNull() { - assertFalse(isExecutable(null)); - } - - @Test - void testIsMember() { - assertTrue(isMember(METHOD)); - assertTrue(isMember(CONSTRUCTOR)); - assertTrue(isMember(STATIC_INIT)); - assertTrue(isMember(INSTANCE_INIT)); - assertTrue(isMember(FIELD)); - assertTrue(isMember(ENUM_CONSTANT)); - assertFalse(isMember(CLASS)); - } - - @Test - void testIsMemberOnNull() { - assertFalse(isMember(null)); - } - - @Test - void testIsInitializer() { - assertTrue(isInitializer(STATIC_INIT)); - assertTrue(isInitializer(INSTANCE_INIT)); - assertFalse(isInitializer(METHOD)); - assertFalse(isInitializer(CONSTRUCTOR)); - assertFalse(isInitializer(CLASS)); - } - - @Test - void testIsInitializerOnNull() { - assertFalse(isInitializer(null)); - } - - @Test - void testIsVariable() { - assertTrue(isVariable(ENUM_CONSTANT)); - assertTrue(isVariable(FIELD)); - assertTrue(isVariable(PARAMETER)); - assertTrue(isVariable(LOCAL_VARIABLE)); - assertTrue(isVariable(EXCEPTION_PARAMETER)); - assertTrue(isVariable(RESOURCE_VARIABLE)); - assertFalse(isVariable(CLASS)); - } - - @Test - void testIsVariableOnNull() { - assertFalse(isVariable(null)); - } - - @Test - void testToElementKind() { - for (ElementType elementType : values()) { - assertElementKind(elementType); - } - } - - @Test - void testToElementKindOnNull() { - assertSame(OTHER, toElementKind(null)); - } - - @Test - void testMatchesElementType() { - for (ElementType elementType : values()) { - assertMatchesElementType(elementType); - } - } - - @Test - void testMatchesElementTypeOnNull() { - assertFalse(matchesElementType(null, (ElementType) null)); - assertFalse(matchesElementType(null, TYPE_USE)); - assertFalse(matchesElementType(toElementKind(TYPE_USE), (ElementType) null)); - } - - @Test - void testMatchesElementTypeWithArray() { - for (ElementType elementType : values()) { - assertTrue(matchesElementType(toElementKind(elementType), values())); - } - } - - @Test - void testMatchesElementTypeWithElement() { - matchesElementType(this.testTypeElement, TYPE); - } - - @Test - void testMatchesElementTypeWithArrayOnNull() { - assertFalse(matchesElementType(NULL_ELEMENT_KIND)); - assertFalse(matchesElementType(NULL_ELEMENT)); - - assertFalse(matchesElementType(NULL_ELEMENT_KIND, (ElementType[]) null)); - assertFalse(matchesElementType(NULL_ELEMENT, (ElementType[]) null)); - - assertFalse(matchesElementType(NULL_ELEMENT_KIND, TYPE_USE, PACKAGE)); - assertFalse(matchesElementType(NULL_ELEMENT, TYPE_USE, PACKAGE)); - - assertFalse(matchesElementType(toElementKind(TYPE_USE), (ElementType[]) null)); - } - - @Test - void testFilterElements() { - assertEmptyList(filterElements(ofList(testTypeElement), alwaysFalse())); - } - - @Test - void testFilterElementsOnNull() { - assertEmptyList(filterElements(NULL_LIST, alwaysTrue())); - List methods = ofList(echoMethod); - assertSame(emptyList(), filterElements(methods, NULL_PREDICATE_ARRAY)); - } - - @Test - void testFilterElementsOnEmpty() { - assertEmptyList(filterElements(emptyList(), alwaysTrue())); - List methods = ofList(echoMethod); - assertEquals(methods, filterElements(methods)); - } - - @Test - void testMatchParameterTypes() { - assertTrue(matchParameterTypes(echoMethod.getParameters(), String.class)); - assertFalse(matchParameterTypes(echoMethod.getParameters(), Object.class)); - } - - @Test - void testMatchParameterTypesOnNull() { - assertFalse(matchParameterTypes(NULL_LIST, String.class)); - assertFalse(matchParameterTypes(emptyList(), NULL_CLASS_ARRAY)); - } - - @Test - void testMatchParameterTypeNames() { - assertTrue(matchParameterTypeNames(echoMethod.getParameters(), "java.lang.String")); - assertFalse(matchParameterTypeNames(echoMethod.getParameters(), "java.lang.Object")); - } - - @Test - void testMatchParameterTypeNamesOnNull() { - assertFalse(matchParameterTypeNames(NULL_LIST, "java.lang.String")); - assertFalse(matchParameterTypeNames(emptyList(), NULL_STRING_ARRAY)); - } - - void assertElementKind(ElementType elementType) { - ElementKind elementKind = toElementKind(elementType); - assertNotNull(elementKind); - } - - void assertMatchesElementType(ElementType elementType) { - assertTrue(matchesElementType(toElementKind(elementType), elementType)); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ExecutableElementComparatorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ExecutableElementComparatorTest.java deleted file mode 100644 index dfd4eaa25..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/ExecutableElementComparatorTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.TestService; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import java.lang.reflect.Type; - -import static io.microsphere.annotation.processor.util.ExecutableElementComparator.INSTANCE; -import static io.microsphere.annotation.processor.util.MethodUtils.findMethod; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * {@link ExecutableElementComparator} Test - * - * @author Mercy - * @see ExecutableElementComparator - * @since 1.0.0 - */ -class ExecutableElementComparatorTest extends AbstractAnnotationProcessingTest { - - private final ExecutableElementComparator comparator = INSTANCE; - - @Test - void testCompareOnSameMethods() { - // Object#toString() - String methodName = "toString"; - ExecutableElement method = getMethod(methodName); - assertEquals(0, comparator.compare(method, method)); - } - - @Test - void testCompareOnDifferentMethods() { - assertEquals("toString".compareTo("hashCode"), comparator.compare(getMethod("toString"), getMethod("hashCode"))); - } - - @Test - void testCompareOnOverloadMethodsWithSameParameterCount() { - // Integer#valueOf(int) | Integer#valueOf(String) - TypeElement typeElement = getTypeElement(Integer.class); - String methodName = "valueOf"; - assertEquals(int.class.getName().compareTo(String.class.getName()), comparator.compare(findMethod(typeElement, methodName, int.class), findMethod(typeElement, methodName, String.class))); - } - - @Test - void testCompareOnOverloadMethodsWithDifferentParameterCount() { - // StringBuilder#append(char[]) | StringBuilder#append(char[],int,int) - TypeElement typeElement = getTypeElement(StringBuilder.class); - String methodName = "append"; - assertEquals(-2, comparator.compare( - findMethod(typeElement, methodName, char[].class), - findMethod(typeElement, methodName, char[].class, int.class, int.class))); - } - - @Test - void testCompare() { - // AutoCloseable#close() - assertEquals(0, comparator.compare(getMethod("close"), - findMethod(getTypeElement(AutoCloseable.class), "close"))); - - // TestService#echo(String) - assertEquals(0, comparator.compare(getMethod("echo", String.class), - findMethod(getTypeElement(TestService.class), "echo", String.class))); - } - - @Override - public boolean equals(Object object) { - return super.equals(object); - } - - private ExecutableElement getMethod(String methodName, Type... parameterTypes) { - return findMethod(testTypeElement, methodName, parameterTypes); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/FieldUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/FieldUtilsTest.java deleted file mode 100644 index 7477f52ee..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/FieldUtilsTest.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.model.Color; -import io.microsphere.annotation.processor.model.Model; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; -import java.io.Serializable; -import java.lang.annotation.ElementType; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static io.microsphere.annotation.processor.util.FieldUtils.equalsFieldName; -import static io.microsphere.annotation.processor.util.FieldUtils.filterDeclaredFields; -import static io.microsphere.annotation.processor.util.FieldUtils.findAllDeclaredFields; -import static io.microsphere.annotation.processor.util.FieldUtils.findDeclaredFields; -import static io.microsphere.annotation.processor.util.FieldUtils.findField; -import static io.microsphere.annotation.processor.util.FieldUtils.getAllDeclaredFields; -import static io.microsphere.annotation.processor.util.FieldUtils.getAllNonStaticFields; -import static io.microsphere.annotation.processor.util.FieldUtils.getDeclaredField; -import static io.microsphere.annotation.processor.util.FieldUtils.getDeclaredFields; -import static io.microsphere.annotation.processor.util.FieldUtils.getNonStaticFields; -import static io.microsphere.annotation.processor.util.FieldUtils.isEnumMemberField; -import static io.microsphere.annotation.processor.util.FieldUtils.isField; -import static io.microsphere.annotation.processor.util.FieldUtils.isNonStaticField; -import static io.microsphere.annotation.processor.util.MethodUtils.findMethod; -import static io.microsphere.lang.function.Predicates.alwaysFalse; -import static io.microsphere.lang.function.Predicates.alwaysTrue; -import static io.microsphere.util.StringUtils.EMPTY_STRING; -import static javax.lang.model.element.Modifier.FINAL; -import static javax.lang.model.element.Modifier.PRIVATE; -import static javax.lang.model.element.Modifier.PUBLIC; -import static javax.lang.model.element.Modifier.STATIC; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * {@link FieldUtils} Test - * - * @author Mercy - * @since 1.0.0 - */ -class FieldUtilsTest extends AbstractAnnotationProcessingTest { - - @Test - void testGetDeclaredField() { - TypeElement type = getTypeElement(Model.class); - testGetDeclaredField(type, "f", float.class); - testGetDeclaredField(type, "d", double.class); - testGetDeclaredField(type, "tu", TimeUnit.class); - testGetDeclaredField(type, "str", String.class); - testGetDeclaredField(type, "bi", BigInteger.class); - testGetDeclaredField(type, "bd", BigDecimal.class); - } - - @Test - void testGetDeclaredFieldOnNotFound() { - TypeElement type = getTypeElement(Model.class); - assertNull(getDeclaredField(type, "b")); - assertNull(getDeclaredField(type, "s")); - assertNull(getDeclaredField(type, "i")); - assertNull(getDeclaredField(type, "l")); - assertNull(getDeclaredField(type, "z")); - } - - @Test - void testGetDeclaredFieldOnNull() { - assertNull(getDeclaredField(NULL_ELEMENT, "z")); - assertNull(getDeclaredField(NULL_TYPE_MIRROR, "z")); - } - - @Test - void testGetDeclaredFields() { - TypeElement type = getTypeElement(Model.class); - List fields = getDeclaredFields(type); - assertModelFields(fields); - - fields = getDeclaredFields(type.asType()); - assertModelFields(fields); - } - - @Test - void testGetDeclaredFieldsOnNull() { - assertTrue(getDeclaredFields(NULL_ELEMENT).isEmpty()); - assertTrue(getDeclaredFields(NULL_TYPE_MIRROR).isEmpty()); - } - - @Test - void testGetAllDeclaredFields() { - TypeElement type = getTypeElement(Model.class); - List fields = getAllDeclaredFields(type); - assertModelAllFields(fields); - } - - @Test - void testGetAllDeclaredFieldsOnNull() { - assertTrue(getAllDeclaredFields(NULL_ELEMENT).isEmpty()); - assertTrue(getAllDeclaredFields(NULL_TYPE_MIRROR).isEmpty()); - } - - @Test - void testFindField() { - TypeElement type = getTypeElement(Model.class); - testFindField(type, "f", float.class); - testFindField(type, "d", double.class); - testFindField(type, "tu", TimeUnit.class); - testFindField(type, "str", String.class); - testFindField(type, "bi", BigInteger.class); - testFindField(type, "bd", BigDecimal.class); - testFindField(type, "b", byte.class); - testFindField(type, "s", short.class); - testFindField(type, "i", int.class); - testFindField(type, "l", long.class); - testFindField(type, "z", boolean.class); - } - - @Test - void testFindFieldOnNull() { - TypeElement type = getTypeElement(Model.class); - assertNull(findField(NULL_ELEMENT, "f")); - assertNull(findField(NULL_ELEMENT, NULL_STRING)); - - assertNull(findField(NULL_TYPE_MIRROR, "f")); - assertNull(findField(NULL_TYPE_MIRROR, NULL_STRING)); - - assertNull(findField(type, NULL_STRING)); - assertNull(findField(type.asType(), NULL_STRING)); - } - - @Test - void testFindDeclaredFields() { - TypeElement type = getTypeElement(Model.class); - - List fields = findAllDeclaredFields(type, alwaysTrue()); - assertModelAllFields(fields); - - fields = findAllDeclaredFields(type, alwaysFalse()); - assertEmptyList(fields); - - fields = findDeclaredFields(type, f -> "f".equals(f.getSimpleName().toString())); - assertEquals(1, fields.size()); - assertEquals("f", fields.get(0).getSimpleName().toString()); - } - - @Test - void testFindDeclaredFieldsOnNull() { - assertEmptyList(findDeclaredFields(NULL_ELEMENT, alwaysTrue())); - assertEmptyList(findDeclaredFields(NULL_TYPE_MIRROR, alwaysTrue())); - } - - @Test - void testFindAllDeclaredFields() { - TypeElement type = getTypeElement(Model.class); - - List fields = findAllDeclaredFields(type, alwaysTrue()); - assertModelAllFields(fields); - - fields = findAllDeclaredFields(type, alwaysFalse()); - assertEmptyList(fields); - - fields = findAllDeclaredFields(type, f -> "f".equals(f.getSimpleName().toString())); - assertEquals(1, fields.size()); - assertEquals("f", fields.get(0).getSimpleName().toString()); - } - - @Test - void testFindAllDeclaredFieldsOnNull() { - assertEmptyList(findAllDeclaredFields(NULL_ELEMENT, alwaysTrue())); - assertEmptyList(findAllDeclaredFields(NULL_TYPE_MIRROR, alwaysTrue())); - } - - @Test - void testFilterDeclaredFieldsOnNull() { - assertFilterDeclaredFieldsReturningEmptyList(NULL_TYPE_MIRROR); - } - - @Test - void testFilterDeclaredFields() { - TypeMirror type = getTypeMirror(Model.class); - List fields = filterDeclaredFields(type, true, alwaysTrue()); - assertModelAllFields(fields); - - fields = filterDeclaredFields(type, true, alwaysFalse()); - assertEmptyList(fields); - - fields = filterDeclaredFields(type, false, alwaysTrue()); - assertModelFields(fields); - - fields = filterDeclaredFields(type, false, alwaysFalse()); - assertEmptyList(fields); - } - - @Test - void testFilterDeclaredFieldsOnNoDeclaredMembers() { - TypeMirror type = getTypeMirror(Serializable.class); - assertFilterDeclaredFieldsReturningEmptyList(type); - } - - @Test - void testFilterDeclaredFieldsOnNoDeclaredFields() { - TypeMirror type = getTypeMirror(Object.class); - assertFilterDeclaredFieldsReturningEmptyList(type); - } - - private void assertFilterDeclaredFieldsReturningEmptyList(TypeMirror type) { - assertEmptyList(filterDeclaredFields(type, true, alwaysTrue())); - assertEmptyList(filterDeclaredFields(type, false, alwaysTrue())); - assertEmptyList(filterDeclaredFields(type, true, alwaysFalse())); - assertEmptyList(filterDeclaredFields(type, false, alwaysFalse())); - assertEmptyList(filterDeclaredFields(type, true, NULL_PREDICATE_ARRAY)); - assertEmptyList(filterDeclaredFields(type, false, NULL_PREDICATE_ARRAY)); - assertEmptyList(filterDeclaredFields(type, true)); - assertEmptyList(filterDeclaredFields(type, false)); - } - - @Test - void testIsEnumField() { - TypeElement type = getTypeElement(Color.class); - - VariableElement field = findField(type, "RED"); - assertTrue(isEnumMemberField(field)); - - field = findField(type, "YELLOW"); - assertTrue(isEnumMemberField(field)); - - field = findField(type, "BLUE"); - assertTrue(isEnumMemberField(field)); - - type = getTypeElement(Model.class); - field = findField(type, "f"); - assertFalse(isEnumMemberField(field)); - - assertFalse(isEnumMemberField(NULL_FIELD)); - } - - @Test - void testIsNonStaticField() { - TypeElement type = getTypeElement(Model.class); - assertTrue(isNonStaticField(findField(type, "f"))); - } - - @Test - void testIsNonStaticFieldOnStaticField() { - TypeElement type = getTypeElement(Color.class); - for (Color color : Color.values()) { - assertFalse(isNonStaticField(findField(type, color.name()))); - } - } - - @Test - void testIsNonStaticFieldOnMethod() { - TypeElement type = getTypeElement(Model.class); - ExecutableElement method = findMethod(type, "setF", float.class); - for (VariableElement parameter : method.getParameters()) { - assertFalse(isNonStaticField(parameter)); - } - } - - @Test - void testIsField() { - TypeElement type = getTypeElement(Model.class); - assertTrue(isField(findField(type, "f"))); - assertTrue(isField(findField(type, "f"), PRIVATE)); - - type = getTypeElement(Color.class); - assertTrue(isField(findField(type, "BLUE"), PUBLIC, STATIC, FINAL)); - } - - @Test - void testIsFieldOnMethod() { - TypeElement type = getTypeElement(Model.class); - ExecutableElement method = findMethod(type, "getF"); - for (VariableElement parameter : method.getParameters()) { - assertFalse(isField(parameter)); - } - } - - @Test - void testIsFieldOnNull() { - assertFalse(isField(NULL_FIELD)); - assertFalse(isField(NULL_FIELD, PUBLIC, STATIC, FINAL)); - - TypeElement type = getTypeElement(Model.class); - assertFalse(isField(findField(type, "f"), NULL_MODIFIER_ARRAY)); - } - - @Test - void testGetNonStaticFields() { - TypeElement type = getTypeElement(Model.class); - - List fields = getNonStaticFields(type); - assertModelFields(fields); - - fields = getNonStaticFields(type.asType()); - assertModelFields(fields); - - assertTrue(getAllNonStaticFields(NULL_ELEMENT).isEmpty()); - assertTrue(getAllNonStaticFields(NULL_TYPE_MIRROR).isEmpty()); - } - - @Test - void testGetNonStaticFieldsOnNull() { - assertTrue(getNonStaticFields(NULL_TYPE_MIRROR).isEmpty()); - assertTrue(getNonStaticFields(NULL_ELEMENT).isEmpty()); - } - - @Test - void testGetNonStaticFieldsOnEnum() { - TypeElement type = getTypeElement(ElementType.class); - List fields = getNonStaticFields(type); - assertEmptyList(fields); - } - - @Test - void testGetAllNonStaticFields() { - TypeElement type = getTypeElement(Model.class); - - List fields = getAllNonStaticFields(type); - assertModelAllFields(fields); - - fields = getAllNonStaticFields(type.asType()); - assertModelAllFields(fields); - - assertTrue(getAllNonStaticFields(NULL_ELEMENT).isEmpty()); - assertTrue(getAllNonStaticFields(NULL_TYPE_MIRROR).isEmpty()); - } - - @Test - void testEqualsFieldName() { - TypeElement type = getTypeElement(Model.class); - String fieldName = "f"; - VariableElement field = findField(type, fieldName); - assertTrue(equalsFieldName(field, fieldName)); - assertFalse(equalsFieldName(field, "d")); - } - - @Test - void testEqualsFieldNameOnNull() { - TypeElement type = getTypeElement(Model.class); - String fieldName = "f"; - VariableElement field = findField(type, fieldName); - - assertFalse(equalsFieldName(NULL_FIELD, EMPTY_STRING)); - assertFalse(equalsFieldName(field, NULL_STRING)); - } - - private void assertModelFields(List fields) { - assertEquals(6, fields.size()); - assertEquals("d", fields.get(1).getSimpleName().toString()); - assertEquals("tu", fields.get(2).getSimpleName().toString()); - assertEquals("str", fields.get(3).getSimpleName().toString()); - assertEquals("bi", fields.get(4).getSimpleName().toString()); - assertEquals("bd", fields.get(5).getSimpleName().toString()); - } - - private void assertModelAllFields(List fields) { - assertEquals(11, fields.size()); - assertEquals("f", fields.get(0).getSimpleName().toString()); - assertEquals("d", fields.get(1).getSimpleName().toString()); - assertEquals("tu", fields.get(2).getSimpleName().toString()); - assertEquals("str", fields.get(3).getSimpleName().toString()); - assertEquals("bi", fields.get(4).getSimpleName().toString()); - assertEquals("bd", fields.get(5).getSimpleName().toString()); - assertEquals("b", fields.get(6).getSimpleName().toString()); - assertEquals("s", fields.get(7).getSimpleName().toString()); - assertEquals("i", fields.get(8).getSimpleName().toString()); - assertEquals("l", fields.get(9).getSimpleName().toString()); - assertEquals("z", fields.get(10).getSimpleName().toString()); - } - - private void testGetDeclaredField(TypeElement type, String fieldName, Type fieldType) { - VariableElement field = getDeclaredField(type, fieldName); - assertField(field, fieldName, fieldType); - - field = getDeclaredField(type.asType(), fieldName); - assertField(field, fieldName, fieldType); - } - - private void testFindField(TypeElement type, String fieldName, Type fieldType) { - VariableElement field = findField(type, fieldName); - assertField(field, fieldName, fieldType); - } - - private void assertField(VariableElement field, String fieldName, Type fieldType) { - assertEquals(fieldName, field.getSimpleName().toString()); - assertEquals(fieldType.getTypeName(), field.asType().toString()); - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/LoggerUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/LoggerUtilsTest.java deleted file mode 100644 index dd436fae9..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/LoggerUtilsTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import org.junit.jupiter.api.Test; - -import static io.microsphere.annotation.processor.util.LoggerUtils.LOGGER; -import static io.microsphere.annotation.processor.util.LoggerUtils.debug; -import static io.microsphere.annotation.processor.util.LoggerUtils.error; -import static io.microsphere.annotation.processor.util.LoggerUtils.info; -import static io.microsphere.annotation.processor.util.LoggerUtils.trace; -import static io.microsphere.annotation.processor.util.LoggerUtils.warn; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -/** - * {@link LoggerUtils} Test - * - * @author Mercy - * @since 1.0.0 - */ -class LoggerUtilsTest { - - @Test - void testLogger() { - assertNotNull(LOGGER); - } - - @Test - void testTrace() { - trace("Hello,World"); - trace("Hello,{}", "World"); - trace("{},{}", "Hello", "World"); - } - - @Test - void testDebug() { - debug("Hello,World"); - debug("Hello,{}", "World"); - debug("{},{}", "Hello", "World"); - } - - @Test - void testInfo() { - info("Hello,World"); - info("Hello,{}", "World"); - info("{},{}", "Hello", "World"); - } - - @Test - void testWarn() { - warn("Hello,World"); - warn("Hello,{}", "World"); - warn("{},{}", "Hello", "World"); - } - - @Test - void testError() { - error("Hello,World"); - error("Hello,{}", "World"); - error("{},{}", "Hello", "World"); - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MemberUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MemberUtilsTest.java deleted file mode 100644 index aa455d3a5..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MemberUtilsTest.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.model.Model; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import java.util.List; - -import static io.microsphere.annotation.processor.util.MemberUtils.findAllDeclaredMembers; -import static io.microsphere.annotation.processor.util.MemberUtils.findDeclaredMembers; -import static io.microsphere.annotation.processor.util.MemberUtils.getAllDeclaredMembers; -import static io.microsphere.annotation.processor.util.MemberUtils.getDeclaredMembers; -import static io.microsphere.lang.function.Predicates.alwaysFalse; -import static io.microsphere.lang.function.Predicates.alwaysTrue; -import static javax.lang.model.util.ElementFilter.fieldsIn; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * {@link MemberUtils} Test - * - * @author Mercy - * @since 1.0.0 - */ -class MemberUtilsTest extends AbstractAnnotationProcessingTest { - - @Test - void testGetDeclaredMembers() { - assertGetDeclaredMembersOfModel(); - } - - @Test - void testGetDeclaredMembersOnNull() { - assertEmptyList(getDeclaredMembers(NULL_TYPE_ELEMENT)); - assertEmptyList(getDeclaredMembers(NULL_TYPE_MIRROR)); - } - - @Test - void testGetAllDeclaredMembers() { - assertGetAllDeclaredMembersOfModel(); - } - - @Test - void testGetAllDeclaredMembersOnNull() { - assertEmptyList(getAllDeclaredMembers(NULL_TYPE_ELEMENT)); - assertEmptyList(getAllDeclaredMembers(NULL_TYPE_MIRROR)); - } - - @Test - void testGetDeclaredMembersOnAll() { - assertGetAllDeclaredMembersOfModel(getDeclaredMembers(getDeclaredType(Model.class), true)); - assertGetAllDeclaredMembersOfModel(getDeclaredMembers(getTypeElement(Model.class), true)); - } - - @Test - void testGetDeclaredMembersOnNotAll() { - assertGetDeclaredMembersOfModel(getDeclaredMembers(getDeclaredType(Model.class), false)); - assertGetDeclaredMembersOfModel(getDeclaredMembers(getTypeElement(Model.class), false)); - } - - @Test - void testFindDeclaredMembers() { - assertFindDeclaredMembersOfModel(); - } - - @Test - void testFindDeclaredMembersOnNull() { - assertEmptyList(findDeclaredMembers(NULL_TYPE_ELEMENT, alwaysTrue())); - assertEmptyList(findDeclaredMembers(NULL_TYPE_ELEMENT, alwaysFalse())); - assertEmptyList(findDeclaredMembers(NULL_TYPE_MIRROR, alwaysTrue())); - assertEmptyList(findDeclaredMembers(NULL_TYPE_MIRROR, alwaysFalse())); - } - - @Test - void testFindAllDeclaredMembers() { - assertFindAllDeclaredMembersOfModel(); - } - - @Test - void testFindAllDeclaredMembersOnNull() { - assertEmptyList(findAllDeclaredMembers(NULL_TYPE_ELEMENT, alwaysTrue())); - assertEmptyList(findAllDeclaredMembers(NULL_TYPE_ELEMENT, alwaysFalse())); - assertEmptyList(findAllDeclaredMembers(NULL_TYPE_MIRROR, alwaysTrue())); - assertEmptyList(findAllDeclaredMembers(NULL_TYPE_MIRROR, alwaysFalse())); - } - - @Test - void testFindDeclaredMembersOnAll() { - assertGetAllDeclaredMembersOfModel(findDeclaredMembers(getDeclaredType(Model.class), true, alwaysTrue())); - assertGetAllDeclaredMembersOfModel(findDeclaredMembers(getTypeElement(Model.class), true, alwaysTrue())); - } - - @Test - void testFindDeclaredMembersOnNotAll() { - assertGetDeclaredMembersOfModel(findDeclaredMembers(getDeclaredType(Model.class), false, alwaysTrue())); - assertGetDeclaredMembersOfModel(findDeclaredMembers(getTypeElement(Model.class), false, alwaysTrue())); - } - - private void assertFindDeclaredMembersOfModel() { - TypeElement type = getTypeElement(Model.class); - assertGetDeclaredMembersOfModel(findDeclaredMembers(type, alwaysTrue(), alwaysTrue())); - assertGetDeclaredMembersOfModel(findDeclaredMembers(type.asType(), alwaysTrue())); - - assertEmptyList(findDeclaredMembers(type, alwaysFalse())); - assertEmptyList(findDeclaredMembers(type.asType(), alwaysFalse())); - } - - private void assertFindAllDeclaredMembersOfModel() { - TypeElement type = getTypeElement(Model.class); - assertGetAllDeclaredMembersOfModel(findAllDeclaredMembers(type, alwaysTrue(), alwaysTrue())); - assertGetAllDeclaredMembersOfModel(findAllDeclaredMembers(type.asType(), alwaysTrue())); - - assertEmptyList(findAllDeclaredMembers(type, alwaysFalse())); - assertEmptyList(findAllDeclaredMembers(type.asType(), alwaysFalse())); - } - - private void assertGetDeclaredMembersOfModel() { - TypeElement type = getTypeElement(Model.class); - assertGetDeclaredMembersOfModel(getDeclaredMembers(type)); - assertGetDeclaredMembersOfModel(getDeclaredMembers(type.asType())); - } - - private void assertGetDeclaredMembersOfModel(List members) { - List fields = fieldsIn(members); - assertEquals(19, members.size()); - assertEquals(6, fields.size()); - assertEquals("f", fields.get(0).getSimpleName().toString()); - assertEquals("d", fields.get(1).getSimpleName().toString()); - assertEquals("tu", fields.get(2).getSimpleName().toString()); - assertEquals("str", fields.get(3).getSimpleName().toString()); - assertEquals("bi", fields.get(4).getSimpleName().toString()); - assertEquals("bd", fields.get(5).getSimpleName().toString()); - } - - private void assertGetAllDeclaredMembersOfModel() { - TypeElement type = getTypeElement(Model.class); - assertGetAllDeclaredMembersOfModel(getAllDeclaredMembers(type)); - assertGetAllDeclaredMembersOfModel(getAllDeclaredMembers(type.asType())); - } - - private void assertGetAllDeclaredMembersOfModel(List members) { - List fields = fieldsIn(members); - assertEquals(11, fields.size()); - assertEquals("f", fields.get(0).getSimpleName().toString()); - assertEquals("d", fields.get(1).getSimpleName().toString()); - assertEquals("tu", fields.get(2).getSimpleName().toString()); - assertEquals("str", fields.get(3).getSimpleName().toString()); - assertEquals("bi", fields.get(4).getSimpleName().toString()); - assertEquals("bd", fields.get(5).getSimpleName().toString()); - assertEquals("b", fields.get(6).getSimpleName().toString()); - assertEquals("s", fields.get(7).getSimpleName().toString()); - assertEquals("i", fields.get(8).getSimpleName().toString()); - assertEquals("l", fields.get(9).getSimpleName().toString()); - assertEquals("z", fields.get(10).getSimpleName().toString()); - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MessagerUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MessagerUtilsTest.java deleted file mode 100644 index 9e7d629ae..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MessagerUtilsTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.annotation.processor.util; - - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import org.junit.jupiter.api.Test; - -import javax.annotation.processing.Messager; - -import static io.microsphere.annotation.processor.util.MessagerUtils.printError; -import static io.microsphere.annotation.processor.util.MessagerUtils.printMandatoryWarning; -import static io.microsphere.annotation.processor.util.MessagerUtils.printMessage; -import static io.microsphere.annotation.processor.util.MessagerUtils.printNote; -import static io.microsphere.annotation.processor.util.MessagerUtils.printWarning; -import static javax.tools.Diagnostic.Kind.OTHER; - -/** - * {@link MessagerUtils} Test - * - * @author Mercy - * @see MessagerUtils - * @see Messager - * @since 1.0.0 - */ -class MessagerUtilsTest extends AbstractAnnotationProcessingTest { - - private Messager messager; - - @Override - protected void beforeTest() { - this.messager = this.processingEnv.getMessager(); - } - - @Test - void testPrintNote() { - printNote(this.processingEnv, "Hello, {}!", "printNote"); - printNote(this.messager, "Hello, {}!", "printNote"); - } - - @Test - void testPrintWarning() { - printWarning(this.processingEnv, "Hello, {}!", "printWarning"); - printWarning(this.messager, "Hello, {}!", "printWarning"); - } - - @Test - void testPrintMandatoryWarning() { - printMandatoryWarning(this.processingEnv, "Hello, {}!", "printMandatoryWarning"); - printMandatoryWarning(this.messager, "Hello, {}!", "printMandatoryWarning"); - } - - @Test - void testPrintError() { - printError(this.processingEnv, "Hello, {}!", "printError"); - printError(this.messager, "Hello, {}!", "printError"); - } - - @Test - void testPrintMessage() { - printMessage(this.processingEnv, OTHER, "Hello, {}!", "printMessage"); - printMessage(this.messager, OTHER, "Hello, {}!", "printMessage"); - } -} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MethodUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MethodUtilsTest.java deleted file mode 100644 index f5e728b85..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/MethodUtilsTest.java +++ /dev/null @@ -1,506 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.TestService; -import io.microsphere.annotation.processor.model.Model; -import io.microsphere.constants.Constants; -import io.microsphere.constants.PropertyConstants; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeMirror; -import java.io.Serializable; -import java.lang.reflect.Type; -import java.util.List; -import java.util.Set; - -import static io.microsphere.annotation.processor.util.ElementUtils.isPublicNonStatic; -import static io.microsphere.annotation.processor.util.MemberUtils.getDeclaredMembers; -import static io.microsphere.annotation.processor.util.MethodUtils.filterMethods; -import static io.microsphere.annotation.processor.util.MethodUtils.findAllDeclaredMethods; -import static io.microsphere.annotation.processor.util.MethodUtils.findDeclaredMethods; -import static io.microsphere.annotation.processor.util.MethodUtils.findMethod; -import static io.microsphere.annotation.processor.util.MethodUtils.findPublicNonStaticMethods; -import static io.microsphere.annotation.processor.util.MethodUtils.getAllDeclaredMethods; -import static io.microsphere.annotation.processor.util.MethodUtils.getDeclaredMethods; -import static io.microsphere.annotation.processor.util.MethodUtils.getEnclosingElement; -import static io.microsphere.annotation.processor.util.MethodUtils.getMethodName; -import static io.microsphere.annotation.processor.util.MethodUtils.getMethodParameterTypeMirrors; -import static io.microsphere.annotation.processor.util.MethodUtils.getMethodParameterTypeNames; -import static io.microsphere.annotation.processor.util.MethodUtils.getOverrideMethod; -import static io.microsphere.annotation.processor.util.MethodUtils.getReturnTypeName; -import static io.microsphere.annotation.processor.util.MethodUtils.isMethod; -import static io.microsphere.annotation.processor.util.MethodUtils.isPublicNonStaticMethod; -import static io.microsphere.annotation.processor.util.MethodUtils.matches; -import static io.microsphere.collection.Lists.ofList; -import static io.microsphere.lang.function.Predicates.alwaysFalse; -import static io.microsphere.lang.function.Predicates.alwaysTrue; -import static io.microsphere.reflect.TypeUtils.getTypeNames; -import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; -import static io.microsphere.util.ArrayUtils.ofArray; -import static java.util.Collections.emptyList; -import static javax.lang.model.element.ElementKind.METHOD; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * {@link MethodUtils} Test - * - * @author Mercy - * @since 1.0.0 - */ -class MethodUtilsTest extends AbstractAnnotationProcessingTest { - - private List objectMethods; - - private int objectMethodsSize; - - @Override - protected void addCompiledClasses(Set> compiledClasses) { - compiledClasses.add(PropertyConstants.class); - } - - @Override - protected void beforeTest() { - super.beforeTest(); - TypeElement type = getTypeElement(Object.class); - List methods = getDeclaredMethods(type); - this.objectMethods = methods; - this.objectMethodsSize = methods.size(); - } - - @Test - void testDeclaredMethods() { - TypeElement type = getTypeElement(Model.class); - List methods = getDeclaredMethods(type); - assertEquals(12, methods.size()); - - methods = getDeclaredMethods(type.asType()); - assertEquals(12, methods.size()); - } - - @Test - void testDeclaredMethodsOnNull() { - assertTrue(getDeclaredMethods(NULL_TYPE_ELEMENT).isEmpty()); - assertTrue(getDeclaredMethods(NULL_TYPE_MIRROR).isEmpty()); - } - - @Test - void testGetAllDeclaredMethods() { - TypeElement type = getTypeElement(Model.class); - List methods = getAllDeclaredMethods(type); - assertEquals(objectMethodsSize + 22, methods.size()); - - methods = getAllDeclaredMethods(type.asType()); - assertEquals(objectMethodsSize + 22, methods.size()); - } - - @Test - void testGetAllDeclaredMethodsOnNull() { - assertTrue(getAllDeclaredMethods(NULL_TYPE_ELEMENT).isEmpty()); - assertTrue(getAllDeclaredMethods(NULL_TYPE_MIRROR).isEmpty()); - } - - @Test - void testFindDeclaredMethods() { - List methods = findDeclaredMethods(testTypeElement, alwaysTrue()); - assertEquals(2, methods.size()); - - methods = findDeclaredMethods(testTypeMirror, alwaysTrue()); - assertEquals(2, methods.size()); - - methods = findDeclaredMethods(testTypeElement, alwaysFalse()); - assertEmptyList(methods); - - methods = findDeclaredMethods(testTypeMirror, alwaysFalse()); - assertEmptyList(methods); - } - - @Test - void testFindDeclaredMethodsOnNoMemberType() { - TypeElement typeElement = getTypeElement(Serializable.class); - List methods = findDeclaredMethods(typeElement, alwaysTrue()); - assertEmptyList(methods); - } - - @Test - void testFindDeclaredMethodsOnNoMethodType() { - TypeElement typeElement = getTypeElement(PropertyConstants.class); - List methods = findDeclaredMethods(typeElement, alwaysTrue()); - assertEmptyList(methods); - } - - @Test - void testFindAllDeclaredMethods() { - List methods = findAllDeclaredMethods(testTypeElement, alwaysTrue()); - assertEquals(objectMethodsSize + 14, methods.size()); - - methods = findAllDeclaredMethods(testTypeMirror, alwaysTrue()); - assertEquals(objectMethodsSize + 14, methods.size()); - - methods = findAllDeclaredMethods(testTypeElement, alwaysFalse()); - assertEmptyList(methods); - - methods = findAllDeclaredMethods(testTypeMirror, alwaysFalse()); - assertEmptyList(methods); - } - - @Test - void testFindAllDeclaredMethodsOnNoMemberType() { - TypeElement typeElement = getTypeElement(Serializable.class); - List methods = findAllDeclaredMethods(typeElement, alwaysTrue()); - assertEmptyList(methods); - } - - @Test - void testFindAllDeclaredMethodsOnNoMethodType() { - TypeElement typeElement = getTypeElement(Constants.class); - List methods = findAllDeclaredMethods(typeElement, alwaysTrue()); - assertEmptyList(methods); - } - - @Test - void testFindAllDeclaredMethodsOnNull() { - assertEmptyList(findAllDeclaredMethods(NULL_TYPE_ELEMENT, alwaysTrue())); - assertEmptyList(findAllDeclaredMethods(NULL_TYPE_ELEMENT, alwaysFalse())); - assertEmptyList(findAllDeclaredMethods(NULL_TYPE_MIRROR, alwaysTrue())); - assertEmptyList(findAllDeclaredMethods(NULL_TYPE_MIRROR, alwaysFalse())); - } - - @Test - void testFindAllDeclaredMethodsWithExcludedTypes() { - List methods = findAllDeclaredMethodsWithoutObjectType(); - assertEquals(14, methods.size()); - } - - @Test - void testFindAllDeclaredMethodsWithExcludedTypesOnNull() { - assertEmptyList(findAllDeclaredMethods(NULL_TYPE_ELEMENT, Object.class)); - assertEmptyList(findAllDeclaredMethods(NULL_TYPE_MIRROR, Object.class)); - } - - @Test - void testFindPublicNonStaticMethods() { - List methods = findPublicNonStaticMethods(testTypeElement, Object.class); - assertEquals(14, methods.size()); - - methods = findPublicNonStaticMethods(testTypeElement.asType(), Object.class); - assertEquals(14, methods.size()); - } - - @Test - void testFindPublicNonStaticMethodsOnNull() { - assertEmptyList(findPublicNonStaticMethods(NULL_TYPE_ELEMENT, Object.class)); - assertEmptyList(findPublicNonStaticMethods(NULL_TYPE_MIRROR, Object.class)); - } - - @Test - void testIsMethod() { - List members = getDeclaredMembers(testTypeElement); - for (Element member : members) { - if (member instanceof ExecutableElement) { - ExecutableElement element = (ExecutableElement) member; - assertEquals(METHOD == member.getKind(), isMethod(element)); - } - } - } - - @Test - void testIsMethodOnNull() { - assertFalse(isMethod(NULL_METHOD)); - } - - @Test - void testIsPublicNonStaticMethod() { - List members = getDeclaredMembers(testTypeElement); - for (Element member : members) { - if (member instanceof ExecutableElement) { - ExecutableElement element = (ExecutableElement) member; - switch (member.getKind()) { - case METHOD: - assertEquals(isPublicNonStaticMethod(element), isPublicNonStatic(element)); - break; - case CONSTRUCTOR: - assertFalse(isPublicNonStaticMethod(element)); - break; - } - } - } - - // Integer#valueOf(String) is a public static method - assertFalse(isPublicNonStaticMethod(findMethod(getTypeElement(Integer.class), "valueOf", String.class))); - - } - - @Test - void testIsPublicNonStaticMethodOnNull() { - assertFalse(isPublicNonStaticMethod(NULL_METHOD)); - } - - @Test - void testFindMethod() { - // Test methods from java.lang.Object - // Object#toString() - Type type = Model.class; - assertFindMethod(type, "toString"); - - // Object#hashCode() - assertFindMethod(type, "hashCode"); - - // Object#getClass() - assertFindMethod(type, "getClass"); - - // Object#finalize() - assertFindMethod(type, "finalize"); - - // Object#clone() - assertFindMethod(type, "clone"); - - // Object#notify() - assertFindMethod(type, "notify"); - - // Object#notifyAll() - assertFindMethod(type, "notifyAll"); - - // Object#wait(long) - assertFindMethod(type, "wait", long.class); - - // Object#wait(long,int) - assertFindMethod(type, "wait", long.class, int.class); - - // Object#equals(Object) - assertFindMethod(type, "equals", Object.class); - } - - @Test - void testFindMethodOnNotFound() { - assertNull(findMethod(testTypeElement, "notFound")); - assertNull(findMethod(testTypeElement, "notFound", String.class)); - assertNull(findMethod(testTypeElement, "notFound", "java.lang.String")); - - assertNull(findMethod(testTypeMirror, "notFound")); - assertNull(findMethod(testTypeMirror, "notFound", String.class)); - assertNull(findMethod(testTypeMirror, "notFound", "java.lang.String")); - } - - @Test - void testFindMethodOnNull() { - assertNull(findMethod(NULL_TYPE_ELEMENT, "toString")); - assertNull(findMethod(NULL_TYPE_ELEMENT, "toString", String.class)); - assertNull(findMethod(NULL_TYPE_ELEMENT, "toString", "java.lang.String")); - assertNull(findMethod(NULL_TYPE_ELEMENT, "toString", NULL_TYPE_ARRAY)); - assertNull(findMethod(NULL_TYPE_ELEMENT, "toString", NULL_STRING_ARRAY)); - - assertNull(findMethod(NULL_TYPE_MIRROR, "toString")); - assertNull(findMethod(NULL_TYPE_MIRROR, "toString", String.class)); - assertNull(findMethod(NULL_TYPE_MIRROR, "toString", "java.lang.String")); - assertNull(findMethod(NULL_TYPE_MIRROR, "toString", NULL_TYPE_ARRAY)); - assertNull(findMethod(NULL_TYPE_MIRROR, "toString", NULL_STRING_ARRAY)); - - assertNull(findMethod(testTypeElement, NULL_STRING)); - assertNull(findMethod(testTypeElement, NULL_STRING, String.class)); - assertNull(findMethod(testTypeElement, NULL_STRING, "java.lang.String")); - assertNull(findMethod(testTypeElement, NULL_STRING, NULL_TYPE_ARRAY)); - assertNull(findMethod(testTypeElement, NULL_STRING, NULL_STRING_ARRAY)); - - assertNull(findMethod(testTypeMirror, NULL_STRING)); - assertNull(findMethod(testTypeMirror, NULL_STRING, String.class)); - assertNull(findMethod(testTypeMirror, NULL_STRING, "java.lang.String")); - assertNull(findMethod(testTypeMirror, NULL_STRING, NULL_TYPE_ARRAY)); - assertNull(findMethod(testTypeMirror, NULL_STRING, NULL_STRING_ARRAY)); - - - assertNull(findMethod(testTypeElement, "toString", NULL_TYPE_ARRAY)); - assertNull(findMethod(testTypeElement, "toString", NULL_STRING_ARRAY)); - - assertNull(findMethod(testTypeMirror, "toString", NULL_TYPE_ARRAY)); - assertNull(findMethod(testTypeMirror, "toString", NULL_STRING_ARRAY)); - } - - @Test - void testGetOverrideMethod() { - List methods = findAllDeclaredMethodsWithoutObjectType(); - - ExecutableElement overrideMethod = getOverrideMethod(processingEnv, testTypeElement, methods.get(0)); - assertNull(overrideMethod); - - ExecutableElement declaringMethod = findMethod(getTypeElement(TestService.class), "echo", "java.lang.String"); - - overrideMethod = getOverrideMethod(processingEnv, testTypeElement, declaringMethod); - assertEquals(methods.get(0), overrideMethod); - } - - @Test - void testFilterMethods() { - - } - - @Test - void testFilterMethodsOnNull() { - assertEmptyList(filterMethods(NULL_LIST, alwaysTrue())); - assertEmptyList(filterMethods(NULL_LIST, NULL_PREDICATE_ARRAY)); - } - - @Test - void testFilterMethodsOnEmpty() { - assertEmptyList(filterMethods(emptyList(), alwaysTrue())); - assertEmptyList(filterMethods(emptyList(), NULL_PREDICATE_ARRAY)); - } - - @Test - void testFilterMethodsOnReturningEmptyList() { - List methods = getDeclaredMethods(testTypeElement); - assertEmptyList(filterMethods(methods, alwaysFalse())); - assertEquals(methods, filterMethods(methods)); - } - - @Test - void testGetMethodName() { - ExecutableElement method = findMethod(testTypeElement, "echo", "java.lang.String"); - assertEquals("echo", getMethodName(method)); - assertNull(getMethodName(NULL_METHOD)); - } - - @Test - void testGetMethodNameOnNull() { - assertNull(getMethodName(NULL_METHOD)); - } - - @Test - void testReturnTypeName() { - ExecutableElement method = findMethod(testTypeElement, "echo", "java.lang.String"); - assertEquals("java.lang.String", getReturnTypeName(method)); - } - - @Test - void testReturnTypeNameOnNull() { - assertNull(getReturnTypeName(NULL_METHOD)); - } - - @Test - void testMatchParameterTypeNames() { - String[] parameterTypeNames = ofArray("java.lang.String"); - ExecutableElement method = findMethod(testTypeElement, "echo", parameterTypeNames); - assertArrayEquals(parameterTypeNames, getMethodParameterTypeNames(method)); - } - - @Test - void testMatchParameterTypeNamesOnNull() { - assertSame(EMPTY_STRING_ARRAY, getMethodParameterTypeNames(NULL_METHOD)); - } - - @Test - void testMatchParameterTypes() { - ExecutableElement method = findMethod(testTypeElement, "toString"); - assertEmptyList(getMethodParameterTypeMirrors(method)); - - method = findMethod(testTypeElement, "equals", Object.class); - List parameterTypes = getMethodParameterTypeMirrors(method); - assertEquals(ofList(parameterTypes.toArray(EMPTY_TYPE_MIRROR_ARRAY)), parameterTypes); - } - - @Test - void testMatchParameterTypesOnNull() { - assertEmptyList(getMethodParameterTypeMirrors(NULL_METHOD)); - } - - @Test - void testMatches() { - assertFindMethod(testClass, "echo", String.class); - } - - @Test - void tstMatchesOnFalse() { - ExecutableElement method = findMethod(testTypeElement, "echo", String.class); - - Type[] parameterTypes = ofArray(String.class, String.class); - String[] parameterTypeNames = getTypeNames(parameterTypes); - assertFalse(matches(method, "echo", parameterTypes)); - assertFalse(matches(method, "echo", parameterTypeNames)); - - parameterTypes = ofArray(Object.class); - parameterTypeNames = getTypeNames(parameterTypes); - assertFalse(matches(method, "echo", parameterTypes)); - assertFalse(matches(method, "echo", parameterTypeNames)); - } - - @Test - void testMatchesOnNull() { - String methodName = "echo"; - Type[] parameterTypes = ofArray(String.class); - String[] parameterTypeNames = getTypeNames(parameterTypes); - ExecutableElement method = findMethod(testTypeElement, methodName, parameterTypes); - - assertFalse(matches(NULL_METHOD, NULL_STRING, parameterTypes)); - assertFalse(matches(method, NULL_STRING, parameterTypes)); - assertFalse(matches(method, methodName, NULL_TYPE_ARRAY)); - - assertFalse(matches(NULL_METHOD, NULL_STRING, parameterTypeNames)); - assertFalse(matches(method, NULL_STRING, parameterTypeNames)); - assertFalse(matches(method, methodName, NULL_STRING_ARRAY)); - } - - @Test - void testGetEnclosingElement() { - String methodName = "echo"; - Type[] parameterTypes = ofArray(String.class); - ExecutableElement method = findMethod(testTypeElement, methodName, parameterTypes); - assertSame(testTypeElement, getEnclosingElement(method)); - } - - @Test - void testGetEnclosingElementOnNull() { - assertNull(getEnclosingElement(NULL_METHOD)); - } - - private void assertFindMethod(Type type, String methodName, Type... parameterTypes) { - TypeElement typeElement = getTypeElement(type); - String[] parameterTypeNames = getTypeNames(parameterTypes); - - ExecutableElement method = findMethod(typeElement, methodName, parameterTypes); - assertMatchesMethod(method, methodName, parameterTypes); - - method = findMethod(typeElement, methodName, parameterTypeNames); - assertMatchesMethod(method, methodName, parameterTypes); - - method = findMethod(typeElement.asType(), methodName, parameterTypes); - assertMatchesMethod(method, methodName, parameterTypes); - - method = findMethod(typeElement.asType(), methodName, parameterTypeNames); - assertMatchesMethod(method, methodName, parameterTypeNames); - } - - private void assertMatchesMethod(ExecutableElement method, String methodName, Type... parameterTypes) { - assertTrue(matches(method, methodName, parameterTypes)); - } - - private void assertMatchesMethod(ExecutableElement method, String methodName, String... parameterTypeNames) { - assertTrue(matches(method, methodName, parameterTypeNames)); - } - - private List findAllDeclaredMethodsWithoutObjectType() { - return findAllDeclaredMethods(testTypeElement, Object.class); - } -} diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/TypeUtilsTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/TypeUtilsTest.java deleted file mode 100644 index 3908724fc..000000000 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/util/TypeUtilsTest.java +++ /dev/null @@ -1,1877 +0,0 @@ -/* - * 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. - */ -package io.microsphere.annotation.processor.util; - -import io.microsphere.annotation.processor.AbstractAnnotationProcessingTest; -import io.microsphere.annotation.processor.DefaultTestService; -import io.microsphere.annotation.processor.GenericTestService; -import io.microsphere.annotation.processor.TestService; -import io.microsphere.annotation.processor.TestServiceImpl; -import io.microsphere.annotation.processor.model.ArrayTypeModel; -import io.microsphere.annotation.processor.model.CollectionTypeModel; -import io.microsphere.annotation.processor.model.Color; -import io.microsphere.annotation.processor.model.MapTypeModel; -import io.microsphere.annotation.processor.model.Model; -import io.microsphere.annotation.processor.model.PrimitiveTypeModel; -import org.junit.jupiter.api.Test; - -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import java.io.Serializable; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Date; -import java.util.EventListener; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; - -import static io.microsphere.annotation.processor.util.FieldUtils.findField; -import static io.microsphere.annotation.processor.util.FieldUtils.getDeclaredFields; -import static io.microsphere.annotation.processor.util.MethodUtils.findMethod; -import static io.microsphere.annotation.processor.util.TypeUtils.findAllDeclaredTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.findAllDeclaredTypesOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.findAllDeclaredTypesOfSuperTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.findAllDeclaredTypesOfSuperclasses; -import static io.microsphere.annotation.processor.util.TypeUtils.findAllTypeElementsOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.findAllTypeElementsOfSuperclasses; -import static io.microsphere.annotation.processor.util.TypeUtils.findAllTypeMirrorsOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.findDeclaredTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.findDeclaredTypesOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.findInterfaceTypeMirror; -import static io.microsphere.annotation.processor.util.TypeUtils.findTypeElements; -import static io.microsphere.annotation.processor.util.TypeUtils.findTypeElementsOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.findTypeMirrorsOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllDeclaredTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllDeclaredTypesOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllDeclaredTypesOfSuperTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllDeclaredTypesOfSuperclasses; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllTypeElements; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllTypeElementsOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllTypeElementsOfSuperTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllTypeElementsOfSuperclasses; -import static io.microsphere.annotation.processor.util.TypeUtils.getAllTypeMirrorsOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.getDeclaredTypeOfSuperclass; -import static io.microsphere.annotation.processor.util.TypeUtils.getDeclaredTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.getDeclaredTypesOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.getTypeElementOfSuperclass; -import static io.microsphere.annotation.processor.util.TypeUtils.getTypeElementsOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.getTypeMirrorsOfInterfaces; -import static io.microsphere.annotation.processor.util.TypeUtils.isAnnotationType; -import static io.microsphere.annotation.processor.util.TypeUtils.isArrayType; -import static io.microsphere.annotation.processor.util.TypeUtils.isClassType; -import static io.microsphere.annotation.processor.util.TypeUtils.isDeclaredType; -import static io.microsphere.annotation.processor.util.TypeUtils.isEnumType; -import static io.microsphere.annotation.processor.util.TypeUtils.isInterfaceType; -import static io.microsphere.annotation.processor.util.TypeUtils.isPrimitiveType; -import static io.microsphere.annotation.processor.util.TypeUtils.isSameType; -import static io.microsphere.annotation.processor.util.TypeUtils.isSimpleType; -import static io.microsphere.annotation.processor.util.TypeUtils.isTypeElement; -import static io.microsphere.annotation.processor.util.TypeUtils.ofDeclaredType; -import static io.microsphere.annotation.processor.util.TypeUtils.ofDeclaredTypes; -import static io.microsphere.annotation.processor.util.TypeUtils.ofTypeElement; -import static io.microsphere.annotation.processor.util.TypeUtils.ofTypeElements; -import static io.microsphere.annotation.processor.util.TypeUtils.ofTypeMirrors; -import static io.microsphere.annotation.processor.util.TypeUtils.typeElementFinder; -import static io.microsphere.collection.ListUtils.ofList; -import static io.microsphere.lang.function.Predicates.alwaysFalse; -import static io.microsphere.lang.function.Predicates.alwaysTrue; -import static io.microsphere.reflect.TypeUtils.getTypeNames; -import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; -import static io.microsphere.util.ArrayUtils.combine; -import static io.microsphere.util.ArrayUtils.length; -import static io.microsphere.util.ArrayUtils.ofArray; -import static java.util.Collections.emptyList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * The {@link TypeUtils} Test - * - * @author Mercy - * @since 1.0.0 - */ -class TypeUtilsTest extends AbstractAnnotationProcessingTest { - - /** - * self type - */ - private static final Class SELF_TYPE = TestServiceImpl.class; - - /** - * super class - */ - private static final Class SUPER_CLASS = GenericTestService.class; - - /** - * all types - */ - private static final Type[] ALL_TYPES = ofArray(SELF_TYPE, SUPER_CLASS, DefaultTestService.class, Object.class, TestService.class, EventListener.class, AutoCloseable.class, Serializable.class); - - /** - * all super types - */ - private static final Type[] ALL_SUPER_TYPES = ofArray(SUPER_CLASS, DefaultTestService.class, Object.class, TestService.class, EventListener.class, AutoCloseable.class, Serializable.class); - - /** - * all super classes - */ - private static final Type[] ALL_SUPER_CLASSES = ofArray(SUPER_CLASS, DefaultTestService.class, Object.class); - - /** - * all super interfaces - */ - private static final Type[] ALL_SUPER_INTERFACES = ofArray(TestService.class, EventListener.class, AutoCloseable.class, Serializable.class); - - /** - * super interfaces - */ - private static final Type[] SUPER_INTERFACES = ofArray(TestService.class, AutoCloseable.class, Serializable.class); - - /** - * super class + super interfaces - */ - private static final Type[] SUPER_TYPES = combine(SUPER_CLASS, SUPER_INTERFACES); - - /** - * self type + all super types = all types - */ - private static final Type[] SELF_TYPE_PLUS_ALL_SUPER_TYPES = combine(SELF_TYPE, ALL_SUPER_TYPES); - - /** - * self type + all super classes - */ - private static final Type[] SELF_TYPE_PLUS_ALL_SUPER_CLASSES = combine(SELF_TYPE, ALL_SUPER_CLASSES); - - /** - * self type + all super interfaces - */ - private static final Type[] SELF_TYPE_PLUS_ALL_SUPER_INTERFACES = combine(SELF_TYPE, ALL_SUPER_INTERFACES); - - /** - * self type + super class - */ - private static final Type[] SELF_TYPE_PLUS_SUPER_CLASS = ofArray(SELF_TYPE, SUPER_CLASS); - - /** - * self type + super interfaces - */ - private static final Type[] SELF_TYPE_PLUS_SUPER_INTERFACES = combine(SELF_TYPE, SUPER_INTERFACES); - - /** - * self type + super class + super interfaces - */ - private static final Type[] SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES = combine(SELF_TYPE, SUPER_TYPES); - - @Override - protected void addCompiledClasses(Set> compiledClasses) { - compiledClasses.add(ArrayTypeModel.class); - compiledClasses.add(CollectionTypeModel.class); - compiledClasses.add(Color.class); - compiledClasses.add(MapTypeModel.class); - } - - @Test - void testIsSimpleType() { - assertTrue(isSimpleType(getTypeElement(Void.class))); - assertTrue(isSimpleType(getTypeElement(Boolean.class))); - assertTrue(isSimpleType(getTypeElement(Character.class))); - assertTrue(isSimpleType(getTypeElement(Byte.class))); - assertTrue(isSimpleType(getTypeElement(Short.class))); - assertTrue(isSimpleType(getTypeElement(Integer.class))); - assertTrue(isSimpleType(getTypeElement(Long.class))); - assertTrue(isSimpleType(getTypeElement(Float.class))); - assertTrue(isSimpleType(getTypeElement(Double.class))); - assertTrue(isSimpleType(getTypeElement(String.class))); - assertTrue(isSimpleType(getTypeElement(BigDecimal.class))); - assertTrue(isSimpleType(getTypeElement(BigInteger.class))); - assertTrue(isSimpleType(getTypeElement(Date.class))); - assertTrue(isSimpleType(getTypeElement(Object.class))); - - assertFalse(isSimpleType(getTypeElement(getClass()))); - } - - @Test - void testIsSimpleTypeOnNull() { - assertFalse(isSimpleType(NULL_TYPE_ELEMENT)); - assertFalse(isSimpleType(NULL_TYPE_MIRROR)); - } - - @Test - void testIsSameType() { - assertIsSameType(testTypeElement, testClass); - - assertFalse(isSameType(getDeclaredType(String.class), "java.lang.Void")); - } - - @Test - void testIsSameTypeOnNull() { - assertFalse(isSameType(NULL_TYPE_MIRROR, testClass)); - assertFalse(isSameType(NULL_TYPE_MIRROR, testClassName)); - assertFalse(isSameType(NULL_ELEMENT, testClass)); - assertFalse(isSameType(NULL_ELEMENT, testClassName)); - - assertFalse(isSameType(testTypeElement, NULL_TYPE)); - assertFalse(isSameType(testTypeElement, NULL_STRING)); - - assertFalse(isSameType(testTypeMirror, NULL_TYPE)); - assertFalse(isSameType(testTypeMirror, NULL_STRING)); - - assertTrue(isSameType(NULL_TYPE_MIRROR, NULL_TYPE)); - assertTrue(isSameType(NULL_TYPE_MIRROR, NULL_STRING)); - assertTrue(isSameType(NULL_ELEMENT, NULL_TYPE)); - assertTrue(isSameType(NULL_ELEMENT, NULL_STRING)); - } - - @Test - void testIsArrayTypeOnTypeMirror() { - assertIsArrayType(ArrayTypeModel.class); - - assertFalse(isArrayType(getTypeMirror(Color.class))); - assertFalse(isArrayType(getTypeMirror(ArrayTypeModel.class))); - } - - @Test - void testIsArrayTypeOnElement() { - assertIsArrayType(getTypeElement(ArrayTypeModel.class)); - - assertFalse(isArrayType(getTypeElement(Color.class))); - assertFalse(isArrayType(getTypeElement(ArrayTypeModel.class))); - } - - @Test - void testIsArrayTypeOnNull() { - assertFalse(isArrayType(NULL_ELEMENT)); - assertFalse(isArrayType(NULL_TYPE_MIRROR)); - } - - @Test - void testIsEnumType() { - assertTrue(isEnumType(getDeclaredType(Color.class))); - assertFalse(isEnumType(getTypeElement(ArrayTypeModel.class))); - } - - @Test - void testIsEnumTypeOnNull() { - assertFalse(isEnumType(NULL_ELEMENT)); - assertFalse(isEnumType(NULL_TYPE_MIRROR)); - } - - @Test - void testIsClassType() { - // class - assertTrue(isClassType(getTypeElement(ArrayTypeModel.class))); - assertTrue(isClassType(getDeclaredType(ArrayTypeModel.class))); - - assertTrue(isClassType(getTypeElement(Model.class))); - assertTrue(isClassType(getDeclaredType(Model.class))); - - // enum - assertFalse(isClassType(getTypeElement(TimeUnit.class))); - assertFalse(isClassType(getDeclaredType(TimeUnit.class))); - - // interface - assertFalse(isClassType(getTypeElement(Serializable.class))); - assertFalse(isClassType(getDeclaredType(Serializable.class))); - } - - @Test - void testIsClassTypeOnNull() { - assertFalse(isClassType(NULL_ELEMENT)); - assertFalse(isClassType(NULL_TYPE_MIRROR)); - } - - @Test - void testIsPrimitiveType() { - TypeElement type = getTypeElement(PrimitiveTypeModel.class); - - getDeclaredFields(type).forEach(t -> { - assertTrue(isPrimitiveType(t)); - assertTrue(isPrimitiveType(t.asType())); - }); - - assertFalse(isPrimitiveType(getTypeElement(ArrayTypeModel.class))); - } - - @Test - void testIsPrimitiveTypeOnNull() { - assertFalse(isPrimitiveType(NULL_ELEMENT)); - assertFalse(isPrimitiveType(NULL_TYPE_MIRROR)); - } - - @Test - void testIsInterfaceType() { - assertTrue(isInterfaceType(getTypeElement(CharSequence.class))); - assertTrue(isInterfaceType(getDeclaredType(CharSequence.class))); - - assertFalse(isInterfaceType(getTypeElement(Model.class))); - assertFalse(isInterfaceType(getDeclaredType(Model.class))); - } - - @Test - void testIsInterfaceTypeOnNull() { - assertFalse(isInterfaceType(NULL_ELEMENT)); - assertFalse(isInterfaceType(NULL_TYPE_MIRROR)); - } - - @Test - void testIsAnnotationType() { - assertTrue(isAnnotationType(getTypeElement(Override.class))); - assertTrue(isAnnotationType(getDeclaredType(Override.class))); - - assertFalse(isAnnotationType(getTypeElement(Model.class))); - assertFalse(isAnnotationType(getDeclaredType(Model.class))); - } - - @Test - void testIsAnnotationTypeOnNull() { - assertFalse(isAnnotationType(NULL_ELEMENT)); - assertFalse(isAnnotationType(NULL_TYPE_MIRROR)); - } - - @Test - void testIsTypeElement() { - assertTrue(isTypeElement(testTypeElement)); - assertTrue(isTypeElement(testTypeMirror)); - assertTrue(isTypeElement(getFieldType(testTypeElement, "context"))); - - // primitive type - assertFalse(isTypeElement(getTypeMirror(int.class))); - } - - @Test - void testIsTypeElementOnNull() { - assertFalse(isTypeElement(NULL_ELEMENT)); - assertFalse(isTypeElement(NULL_TYPE_MIRROR)); - } - - @Test - void testIsDeclaredType() { - assertTrue(isDeclaredType(testTypeElement)); - assertTrue(isDeclaredType(testTypeMirror)); - assertFalse(isDeclaredType(types.getNullType())); - assertFalse(isDeclaredType(types.getPrimitiveType(TypeKind.BYTE))); - assertFalse(isDeclaredType(types.getArrayType(types.getPrimitiveType(TypeKind.BYTE)))); - - // field - assertFalse(isDeclaredType(findField(getTypeMirror(PrimitiveTypeModel.class), "z"))); - - // method - assertFalse(isDeclaredType(findMethod(testTypeElement, "close"))); - } - - @Test - void testIsDeclaredTypeOnNull() { - assertFalse(isDeclaredType(NULL_ELEMENT)); - assertFalse(isDeclaredType(NULL_TYPE_MIRROR)); - } - - @Test - void testOfTypeElement() { - assertEquals(testTypeElement, ofTypeElement(testTypeElement)); - assertEquals(testTypeElement, ofTypeElement(testTypeMirror)); - } - - @Test - void testOfTypeElementOnNull() { - assertNull(ofTypeElement(NULL_ELEMENT)); - assertNull(ofTypeElement(NULL_TYPE_MIRROR)); - } - - @Test - void testOfDeclaredType() { - assertEquals(testTypeMirror, testDeclaredType); - assertEquals(testTypeMirror, ofDeclaredType(testTypeMirror)); - assertEquals(testDeclaredType, ofDeclaredType(testTypeMirror)); - } - - @Test - void testOfDeclaredTypeOnNull() { - assertNull(ofDeclaredType(NULL_ELEMENT)); - assertNull(ofDeclaredType(NULL_TYPE_MIRROR)); - } - - @Test - void testOfTypeMirrors() { - assertOfTypeMirrors(String.class, SELF_TYPE, Color.class); - } - - @Test - void testOfTypeMirrorsOnNull() { - assertEmptyList(ofTypeMirrors(EMPTY_ELEMENT_ARRAY)); - assertEmptyList(ofTypeMirrors(NULL_COLLECTION)); - } - - @Test - void testOfTypeMirrorsOnEmpty() { - assertEmptyList(ofTypeMirrors(EMPTY_ELEMENT_ARRAY)); - assertEmptyList(ofTypeMirrors(emptyList())); - } - - @Test - void testOfTypeElements() { - assertOfTypeElements(String.class, SELF_TYPE, Color.class); - } - - @Test - void testOfTypeElementsOnNull() { - assertEmptyList(ofTypeElements(NULL_TYPE_MIRROR_ARRAY)); - assertEmptyList(ofTypeElements(NULL_COLLECTION)); - } - - @Test - void testOfTypeElementsOnEmpty() { - assertEmptyList(ofTypeElements(EMPTY_TYPE_MIRROR_ARRAY)); - assertEmptyList(ofTypeElements(emptyList())); - } - - @Test - void testOfDeclaredTypes() { - assertOfDeclaredTypes(String.class, SELF_TYPE, Color.class); - } - - @Test - void testOfDeclaredTypesWithFilter() { - List declaredTypes = ofDeclaredTypes(ofList(getTypeElement(String.class), getTypeElement(TestServiceImpl.class), getTypeElement(Color.class)), t -> true); - assertDeclaredTypes(declaredTypes, String.class, SELF_TYPE, Color.class); - } - - @Test - void testOfDeclaredTypesOnNull() { - assertEmptyList(ofDeclaredTypes(NULL_ELEMENT_ARRAY)); - assertEmptyList(ofDeclaredTypes(NULL_COLLECTION)); - } - - @Test - void testOfDeclaredTypesOnEmpty() { - assertEmptyList(ofDeclaredTypes(emptyList())); - } - - @Test - void testGetTypeElementOfSuperclass() { - TypeElement superTypeElement = getTypeElementOfSuperclass(testTypeElement); - assertEquals(getTypeElement(GenericTestService.class), superTypeElement); - - superTypeElement = getTypeElementOfSuperclass(superTypeElement); - assertEquals(getTypeElement(DefaultTestService.class), superTypeElement); - - superTypeElement = getTypeElementOfSuperclass(superTypeElement); - assertEquals(getTypeElement(Object.class), superTypeElement); - - assertNull(getTypeElementOfSuperclass(superTypeElement)); - } - - @Test - void testGetTypeElementOfSuperclassOnNull() { - assertNull(getTypeElementOfSuperclass(NULL_TYPE_ELEMENT)); - } - - @Test - void testGetAllTypeElementsOfSuperclasses() { - List allSuperTypes = getAllTypeElementsOfSuperclasses(testTypeElement); - assertTypeElements(allSuperTypes, ALL_SUPER_CLASSES); - } - - @Test - void testGetAllTypeElementsOfSuperclassesOnNull() { - assertEmptyList(getAllTypeElementsOfSuperclasses(NULL_TYPE_ELEMENT)); - } - - @Test - void testGetTypeElementsOfInterfaces() { - List typeElements = getTypeElementsOfInterfaces(testTypeElement); - assertTypeElements(typeElements, SUPER_INTERFACES); - } - - @Test - void testGetTypeElementsOfInterfacesOnNull() { - assertEmptyList(getTypeElementsOfInterfaces(NULL_TYPE_ELEMENT)); - } - - @Test - void testGetAllTypeElementsOfInterfaces() { - List typeElements = getAllTypeElementsOfInterfaces(testTypeElement); - assertTypeElements(typeElements, ALL_SUPER_INTERFACES); - } - - @Test - void testGetAllTypeElementsOfInterfacesOnNull() { - assertEmptyList(getAllTypeElementsOfInterfaces(NULL_TYPE_ELEMENT)); - } - - @Test - void testGetAllTypeElements() { - List allTypeElements = getAllTypeElements(testTypeElement); - assertTypeElements(allTypeElements, ALL_TYPES); - } - - @Test - void testGetAllTypeElementsOnNull() { - assertEmptyList(getAllTypeElements(NULL_TYPE_ELEMENT)); - } - - @Test - void testGetTypeElementsWithNoArgument() { - List typeElements = TypeUtils.getTypeElements(testTypeElement); - assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testGetTypeElements() { - // true true true true : all types - List typeElements = TypeUtils.getTypeElements(testTypeElement, true, true, true, true); - assertTypeElements(typeElements, ALL_TYPES); - assertEquals(getAllTypeElements(testTypeElement), typeElements); - - // true true true false : self type + all super classes - typeElements = TypeUtils.getTypeElements(testTypeElement, true, true, true, false); - assertTypeElements(typeElements, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - - // true true false true : self type + all super interfaces - typeElements = TypeUtils.getTypeElements(testTypeElement, true, true, false, true); - assertTypeElements(typeElements, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - - // true true false false : self type - typeElements = TypeUtils.getTypeElements(testTypeElement, true, true, false, false); - assertTypeElements(typeElements, SELF_TYPE); - - // true false true true : self type + super class + super interfaces - typeElements = TypeUtils.getTypeElements(testTypeElement, true, false, true, true); - assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - - // true false true false : self type + super class - typeElements = TypeUtils.getTypeElements(testTypeElement, true, false, true, false); - assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS); - - // true false false true : self type + super interfaces - typeElements = TypeUtils.getTypeElements(testTypeElement, true, false, false, true); - assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_INTERFACES); - - // true false false false : self type - typeElements = TypeUtils.getTypeElements(testTypeElement, true, false, false, false); - assertTypeElements(typeElements, SELF_TYPE); - - // false true true true : all super types - typeElements = TypeUtils.getTypeElements(testTypeElement, false, true, true, true); - assertTypeElements(typeElements, ALL_SUPER_TYPES); - assertEquals(getAllTypeElementsOfSuperTypes(testTypeElement), typeElements); - - // false true true false : all super classes - typeElements = TypeUtils.getTypeElements(testTypeElement, false, true, true, false); - assertTypeElements(typeElements, ALL_SUPER_CLASSES); - assertEquals(getAllTypeElementsOfSuperclasses(testTypeElement), typeElements); - - // false true false true : all super interfaces - typeElements = TypeUtils.getTypeElements(testTypeElement, false, true, false, true); - assertTypeElements(typeElements, ALL_SUPER_INTERFACES); - assertEquals(getAllTypeElementsOfInterfaces(testTypeElement), typeElements); - - // false true false false : nothing - typeElements = TypeUtils.getTypeElements(testTypeElement, false, true, false, false); - assertTypeElements(typeElements); - assertEmptyList(typeElements); - - // false false true true : super class + super interfaces - typeElements = TypeUtils.getTypeElements(testTypeElement, false, false, true, true); - assertTypeElements(typeElements, SUPER_TYPES); - - // false false true false : super class - typeElements = TypeUtils.getTypeElements(testTypeElement, false, false, true, false); - assertTypeElements(typeElements, SUPER_CLASS); - assertEquals(ofList(getTypeElementOfSuperclass(testTypeElement)), typeElements); - - // false false false true : super interfaces - typeElements = TypeUtils.getTypeElements(testTypeElement, false, false, false, true); - assertTypeElements(typeElements, SUPER_INTERFACES); - assertEquals(ofList(getTypeElementsOfInterfaces(testTypeElement)), typeElements); - - // false false false false : nothing - typeElements = TypeUtils.getTypeElements(testTypeElement, false, false, false, false); - assertTypeElements(typeElements); - assertEmptyList(typeElements); - } - - @Test - void testGetTypeElementsOnNull() { - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, true, true, true)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, true, true, false)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, true, false, true)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, true, false, false)); - - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, false, true, true)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, true, false)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, false, true)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, false, false)); - - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, true, true, true)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, true, true, false)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, true, false, true)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, true, false, false)); - - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, true, true)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, true, false)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, false, true)); - assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, false, false)); - } - - @Test - void testFindAllTypeElementsOfSuperclasses() { - List typeElements = findAllTypeElementsOfSuperclasses(testTypeElement, alwaysTrue()); - assertTypeElements(typeElements, ALL_SUPER_CLASSES); - assertEquals(getAllTypeElementsOfSuperclasses(testTypeElement), typeElements); - - assertEmptyList(findAllTypeElementsOfSuperclasses(testTypeElement, alwaysFalse())); - } - - @Test - void testFindAllTypeElementsOfSuperclassesOnNull() { - assertEmptyList(findAllTypeElementsOfSuperclasses(NULL_TYPE_ELEMENT, alwaysTrue())); - assertEmptyList(findAllTypeElementsOfSuperclasses(NULL_TYPE_ELEMENT, alwaysFalse())); - } - - @Test - void testFindAllTypeElementsOfInterfaces() { - List typeElements = findAllTypeElementsOfInterfaces(testTypeElement, alwaysTrue()); - assertTypeElements(typeElements, ALL_SUPER_INTERFACES); - assertEquals(getAllTypeElementsOfInterfaces(testTypeElement), typeElements); - - assertEmptyList(findAllTypeElementsOfInterfaces(testTypeElement, alwaysFalse())); - } - - @Test - void testFindAllTypeElementsOfInterfacesOnNull() { - assertEmptyList(findAllTypeElementsOfInterfaces(NULL_TYPE_ELEMENT, alwaysTrue())); - assertEmptyList(findAllTypeElementsOfInterfaces(NULL_TYPE_ELEMENT, alwaysFalse())); - } - - @Test - void testFindTypeElementsOfInterfaces() { - List typeElements = findTypeElementsOfInterfaces(testTypeElement, alwaysTrue()); - assertTypeElements(typeElements, SUPER_INTERFACES); - assertEquals(getTypeElementsOfInterfaces(testTypeElement), typeElements); - - assertEmptyList(findTypeElementsOfInterfaces(testTypeElement, alwaysFalse())); - } - - @Test - void testFindTypeElementsOfInterfacesOnNull() { - assertEmptyList(findTypeElementsOfInterfaces(NULL_TYPE_ELEMENT, alwaysTrue())); - assertEmptyList(findTypeElementsOfInterfaces(NULL_TYPE_ELEMENT, alwaysFalse())); - } - - @Test - void testFindTypeElements() { - // true true true true : all types - List typeElements = findTypeElements(testTypeElement, true, true, true, true, alwaysTrue()); - assertTypeElements(typeElements, ALL_TYPES); - assertEquals(getAllTypeElements(testTypeElement), typeElements); - - // true true true false : self type + all super classes - typeElements = findTypeElements(testTypeElement, true, true, true, false, alwaysTrue()); - assertTypeElements(typeElements, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - - // true true false true : self type + all super interfaces - typeElements = findTypeElements(testTypeElement, true, true, false, true, alwaysTrue()); - assertTypeElements(typeElements, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - - // true true false false : self type - typeElements = findTypeElements(testTypeElement, true, true, false, false, alwaysTrue()); - assertTypeElements(typeElements, SELF_TYPE); - - // true false true true : self type + super class + super interfaces - typeElements = findTypeElements(testTypeElement, true, false, true, true, alwaysTrue()); - assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - - // true false true false : self type + super class - typeElements = findTypeElements(testTypeElement, true, false, true, false, alwaysTrue()); - assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS); - - // true false false true : self type + super interfaces - typeElements = findTypeElements(testTypeElement, true, false, false, true, alwaysTrue()); - assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_INTERFACES); - - // true false false false : self type - typeElements = findTypeElements(testTypeElement, true, false, false, false, alwaysTrue()); - assertTypeElements(typeElements, SELF_TYPE); - - // false true true true : all super types - typeElements = findTypeElements(testTypeElement, false, true, true, true, alwaysTrue()); - assertTypeElements(typeElements, ALL_SUPER_TYPES); - assertEquals(getAllTypeElementsOfSuperTypes(testTypeElement), typeElements); - - // false true true false : all super classes - typeElements = findTypeElements(testTypeElement, false, true, true, false, alwaysTrue()); - assertTypeElements(typeElements, ALL_SUPER_CLASSES); - assertEquals(getAllTypeElementsOfSuperclasses(testTypeElement), typeElements); - - // false true false true : all super interfaces - typeElements = findTypeElements(testTypeElement, false, true, false, true, alwaysTrue()); - assertTypeElements(typeElements, ALL_SUPER_INTERFACES); - assertEquals(getAllTypeElementsOfInterfaces(testTypeElement), typeElements); - - // false true false false : nothing - typeElements = findTypeElements(testTypeElement, false, true, false, false, alwaysTrue()); - assertTypeElements(typeElements); - assertEmptyList(typeElements); - - // false false true true : super types - typeElements = findTypeElements(testTypeElement, false, false, true, true, alwaysTrue()); - assertTypeElements(typeElements, SUPER_TYPES); - - // false false true false : super class - typeElements = findTypeElements(testTypeElement, false, false, true, false, alwaysTrue()); - assertTypeElements(typeElements, SUPER_CLASS); - assertEquals(ofList(getTypeElementOfSuperclass(testTypeElement)), typeElements); - - // false false false true : super interfaces - typeElements = findTypeElements(testTypeElement, false, false, false, true, alwaysTrue()); - assertTypeElements(typeElements, SUPER_INTERFACES); - assertEquals(ofList(getTypeElementsOfInterfaces(testTypeElement)), typeElements); - - // false false false false : nothing - typeElements = findTypeElements(testTypeElement, false, false, false, false, alwaysTrue()); - assertTypeElements(typeElements); - assertEmptyList(typeElements); - } - - @Test - void testFindTypeElementsOnNullFilterElement() { - assertThrows(IllegalArgumentException.class, - () -> findTypeElements(testTypeElement, true, true, true, true, new Predicate[]{null})); - } - - - @Test - void testGetDeclaredTypeOfSuperclass() { - DeclaredType superDeclaredType = getDeclaredTypeOfSuperclass(testTypeMirror); - assertEquals(superDeclaredType, getTypeElement(GenericTestService.class).asType()); - - superDeclaredType = getDeclaredTypeOfSuperclass(superDeclaredType); - assertEquals(superDeclaredType, getTypeElement(DefaultTestService.class).asType()); - - superDeclaredType = getDeclaredTypeOfSuperclass(superDeclaredType); - assertEquals(superDeclaredType, getTypeElement(Object.class).asType()); - - assertNull(getDeclaredTypeOfSuperclass(superDeclaredType)); - } - - @Test - void testGetDeclaredTypeOfSuperclassOnNull() { - assertNull(getDeclaredTypeOfSuperclass(NULL_ELEMENT)); - assertNull(getDeclaredTypeOfSuperclass(NULL_TYPE_MIRROR)); - } - - @Test - void testGetDeclaredTypesOfInterfaces() { - List declaredTypes = getDeclaredTypesOfInterfaces(testTypeMirror); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - } - - @Test - void testGetDeclaredTypesOfInterfacesOnNull() { - assertEmptyList(getDeclaredTypesOfInterfaces(NULL_ELEMENT)); - assertEmptyList(getDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR)); - } - - @Test - void testGetAllDeclaredTypesOfSuperclasses() { - List declaredTypes = getAllDeclaredTypesOfSuperclasses(testTypeMirror); - assertDeclaredTypes(declaredTypes, ALL_SUPER_CLASSES); - } - - @Test - void testGetAllDeclaredTypesOfSuperclassesOnNull() { - assertEmptyList(getAllDeclaredTypesOfSuperclasses(NULL_ELEMENT)); - assertEmptyList(getAllDeclaredTypesOfSuperclasses(NULL_TYPE_MIRROR)); - } - - @Test - void testGetAllDeclaredTypesOfInterfaces() { - List declaredTypes = getAllDeclaredTypesOfInterfaces(testTypeMirror); - assertDeclaredTypes(declaredTypes, ALL_SUPER_INTERFACES); - } - - @Test - void testGetAllDeclaredTypesOfInterfacesOnNull() { - assertEmptyList(getAllDeclaredTypesOfInterfaces(NULL_ELEMENT)); - assertEmptyList(getAllDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR)); - } - - @Test - void testGetAllDeclaredTypesOfSuperTypes() { - List declaredTypes = getAllDeclaredTypesOfSuperTypes(testTypeMirror); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - } - - @Test - void testGetAllDeclaredTypesOfSuperTypesOnNull() { - assertEmptyList(getAllDeclaredTypesOfSuperTypes(NULL_ELEMENT)); - assertEmptyList(getAllDeclaredTypesOfSuperTypes(NULL_TYPE_MIRROR)); - } - - @Test - void testGetAllDeclaredTypes() { - List declaredTypes = getAllDeclaredTypes(testTypeMirror); - assertDeclaredTypes(declaredTypes, ALL_TYPES); - } - - @Test - void testGetAllDeclaredTypesOnNull() { - assertEmptyList(getAllDeclaredTypes(NULL_ELEMENT)); - assertEmptyList(getAllDeclaredTypes(NULL_TYPE_MIRROR)); - } - - @Test - void testGetDeclaredTypes() { - // true true true true : all types - List declaredTypes = getDeclaredTypes(testTypeElement, true, true, true, true); - assertDeclaredTypes(declaredTypes, ALL_TYPES); - assertEquals(getAllDeclaredTypes(testTypeElement), declaredTypes); - - // true true true false : self type + all super classes - declaredTypes = getDeclaredTypes(testTypeElement, true, true, true, false); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - - // true true false true : self type + all super interfaces - declaredTypes = getDeclaredTypes(testTypeElement, true, true, false, true); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - - // true true false false : self type - declaredTypes = getDeclaredTypes(testTypeElement, true, true, false, false); - assertDeclaredTypes(declaredTypes, SELF_TYPE); - - // true false true true : self type + super class + super interfaces - declaredTypes = getDeclaredTypes(testTypeElement, true, false, true, true); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - - // true false true false : self type + super class - declaredTypes = getDeclaredTypes(testTypeElement, true, false, true, false); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_CLASS); - - // true false false true : self type + super interfaces - declaredTypes = getDeclaredTypes(testTypeElement, true, false, false, true); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_INTERFACES); - - // true false false false : self type - declaredTypes = getDeclaredTypes(testTypeElement, true, false, false, false); - assertDeclaredTypes(declaredTypes, SELF_TYPE); - - // false true true true : all super types - declaredTypes = getDeclaredTypes(testTypeElement, false, true, true, true); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - assertEquals(getAllDeclaredTypesOfSuperTypes(testTypeElement), declaredTypes); - - // false true true false : all super classes - declaredTypes = getDeclaredTypes(testTypeElement, false, true, true, false); - assertDeclaredTypes(declaredTypes, ALL_SUPER_CLASSES); - assertEquals(getAllDeclaredTypesOfSuperclasses(testTypeElement), declaredTypes); - - // false true false true : all super interfaces - declaredTypes = getDeclaredTypes(testTypeElement, false, true, false, true); - assertDeclaredTypes(declaredTypes, ALL_SUPER_INTERFACES); - assertEquals(getAllDeclaredTypesOfInterfaces(testTypeElement), declaredTypes); - - // false true false false : nothing - declaredTypes = getDeclaredTypes(testTypeElement, false, true, false, false); - assertDeclaredTypes(declaredTypes); - assertEmptyList(declaredTypes); - - // false false true true : super class + super interfaces - declaredTypes = getDeclaredTypes(testTypeElement, false, false, true, true); - assertDeclaredTypes(declaredTypes, SUPER_TYPES); - - // false false true false : super class - declaredTypes = getDeclaredTypes(testTypeElement, false, false, true, false); - assertDeclaredTypes(declaredTypes, SUPER_CLASS); - assertEquals(ofList(getDeclaredTypeOfSuperclass(testTypeElement)), declaredTypes); - - // false false false true : super interfaces - declaredTypes = getDeclaredTypes(testTypeElement, false, false, false, true); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - assertEquals(ofList(getDeclaredTypesOfInterfaces(testTypeElement)), declaredTypes); - - // false false false false : nothing - declaredTypes = getDeclaredTypes(testTypeElement, false, false, false, false); - assertDeclaredTypes(declaredTypes); - assertEmptyList(declaredTypes); - } - - @Test - void testFindDeclaredTypesWithExcludedTypes() { - List declaredTypes = findDeclaredTypes(testTypeElement, SUPER_CLASS); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - - declaredTypes = findDeclaredTypes(testTypeElement, getTypeNames(SUPER_CLASS)); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - - declaredTypes = findDeclaredTypes(testTypeElement, SUPER_INTERFACES); - assertDeclaredTypes(declaredTypes, SUPER_CLASS); - - declaredTypes = findDeclaredTypes(testTypeElement, getTypeNames(SUPER_INTERFACES)); - assertDeclaredTypes(declaredTypes, SUPER_CLASS); - - declaredTypes = findDeclaredTypes(testTypeMirror, SUPER_CLASS); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - - declaredTypes = findDeclaredTypes(testTypeMirror, getTypeNames(SUPER_CLASS)); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - - declaredTypes = findDeclaredTypes(testTypeMirror, SUPER_INTERFACES); - assertDeclaredTypes(declaredTypes, SUPER_CLASS); - - declaredTypes = findDeclaredTypes(testTypeMirror, getTypeNames(SUPER_INTERFACES)); - assertDeclaredTypes(declaredTypes, SUPER_CLASS); - } - - @Test - void testFindDeclaredTypesWithExcludedTypesOnNull() { - assertEmptyList(findDeclaredTypes(NULL_ELEMENT, NULL_TYPE_ARRAY)); - assertEmptyList(findDeclaredTypes(NULL_ELEMENT, EMPTY_TYPE_ARRAY)); - assertEmptyList(findDeclaredTypes(NULL_ELEMENT, ALL_TYPES)); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, NULL_TYPE_ARRAY)); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, EMPTY_TYPE_ARRAY)); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, ALL_TYPES)); - } - - @Test - void testFindDeclaredTypesWithExcludedTypeNamesOnNull() { - assertEmptyList(findDeclaredTypes(NULL_ELEMENT, NULL_STRING_ARRAY)); - assertEmptyList(findDeclaredTypes(NULL_ELEMENT, EMPTY_STRING_ARRAY)); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, NULL_STRING_ARRAY)); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, EMPTY_STRING_ARRAY)); - } - - @Test - void testFindDeclaredTypesOfInterfaces() { - List declaredTypes = findDeclaredTypesOfInterfaces(testTypeMirror, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - - findDeclaredTypesOfInterfaces(testTypeElement, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - - declaredTypes = findDeclaredTypesOfInterfaces(testTypeMirror, alwaysFalse()); - assertEmptyList(declaredTypes); - - declaredTypes = findDeclaredTypesOfInterfaces(testTypeElement, alwaysFalse()); - assertEmptyList(declaredTypes); - } - - @Test - void testFindDeclaredTypesOfInterfacesOnNull() { - assertEmptyList(findDeclaredTypesOfInterfaces(NULL_ELEMENT, alwaysTrue())); - assertEmptyList(findDeclaredTypesOfInterfaces(NULL_ELEMENT, alwaysFalse())); - assertEmptyList(findDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue())); - assertEmptyList(findDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue())); - } - - @Test - void testFindAllDeclaredTypesOfSuperclasses() { - List declaredTypes = findAllDeclaredTypesOfSuperclasses(testTypeElement, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_SUPER_CLASSES); - - declaredTypes = findAllDeclaredTypesOfSuperclasses(testTypeMirror, alwaysFalse()); - assertEmptyList(declaredTypes); - } - - @Test - void testFindAllDeclaredTypesOfSuperclassesOnNull() { - assertEmptyList(findAllDeclaredTypesOfSuperclasses(NULL_ELEMENT, alwaysTrue())); - assertEmptyList(findAllDeclaredTypesOfSuperclasses(NULL_ELEMENT, alwaysFalse())); - assertEmptyList(findAllDeclaredTypesOfSuperclasses(NULL_TYPE_MIRROR, alwaysTrue())); - assertEmptyList(findAllDeclaredTypesOfSuperclasses(NULL_TYPE_MIRROR, alwaysFalse())); - } - - @Test - void testFindAllDeclaredTypesOfInterfaces() { - List declaredTypes = findAllDeclaredTypesOfInterfaces(testTypeElement, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_SUPER_INTERFACES); - - declaredTypes = findAllDeclaredTypesOfInterfaces(testTypeMirror, alwaysFalse()); - assertEmptyList(declaredTypes); - } - - @Test - void testFindAllDeclaredTypesOfInterfacesOnNull() { - assertEmptyList(findAllDeclaredTypesOfInterfaces(NULL_ELEMENT, alwaysTrue())); - assertEmptyList(findAllDeclaredTypesOfInterfaces(NULL_ELEMENT, alwaysFalse())); - assertEmptyList(findAllDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue())); - assertEmptyList(findAllDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR, alwaysFalse())); - } - - @Test - void testFindAllDeclaredTypesOfSuperTypes() { - List declaredTypes = findAllDeclaredTypesOfSuperTypes(testTypeMirror, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - - findAllDeclaredTypesOfSuperTypes(testTypeElement, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - - declaredTypes = findAllDeclaredTypesOfSuperTypes(testTypeMirror, alwaysFalse()); - assertEmptyList(declaredTypes); - - declaredTypes = findAllDeclaredTypesOfSuperTypes(testTypeElement, alwaysFalse()); - assertEmptyList(declaredTypes); - } - - @Test - void testFindAllDeclaredTypesOfSuperTypesOnNull() { - assertEmptyList(findAllDeclaredTypesOfSuperTypes(NULL_ELEMENT)); - assertEmptyList(findAllDeclaredTypesOfSuperTypes(NULL_TYPE_MIRROR)); - } - - @Test - void testFindAllDeclaredTypes() { - List declaredTypes = findAllDeclaredTypes(testTypeElement, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_TYPES); - - declaredTypes = findAllDeclaredTypes(testTypeMirror, alwaysFalse()); - assertEmptyList(declaredTypes); - } - - @Test - void testFindAllDeclaredTypesOnNull() { - assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, alwaysTrue())); - assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, alwaysFalse())); - assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, alwaysTrue())); - assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, alwaysFalse())); - } - - @Test - void testFindAllDeclaredTypesWithExcludedTypes() { - List declaredTypes = findAllDeclaredTypes(testTypeElement, testClass); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - - - declaredTypes = findAllDeclaredTypes(testTypeMirror, testClass); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - } - - @Test - void testFindAllDeclaredTypesWithExcludedTypeNames() { - List declaredTypes = findAllDeclaredTypes(testTypeElement, testClassName); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - - declaredTypes = findAllDeclaredTypes(testTypeMirror, testClassName); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - } - - @Test - void testFindAllDeclaredTypesWithExcludedTypesOnNull() { - assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, NULL_TYPE_ARRAY)); - assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, EMPTY_TYPE_ARRAY)); - assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, ALL_TYPES)); - assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, NULL_TYPE_ARRAY)); - assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, EMPTY_TYPE_ARRAY)); - assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, ALL_TYPES)); - } - - @Test - void testFindAllDeclaredTypesWithExcludedTypeNamesOnNull() { - assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, NULL_STRING_ARRAY)); - assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, EMPTY_STRING_ARRAY)); - assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, NULL_STRING_ARRAY)); - assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, EMPTY_STRING_ARRAY)); - } - - @Test - void testFindDeclaredTypes() { - // true true true true : all types - List declaredTypes = findDeclaredTypes(testTypeElement, true, true, true, true, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_TYPES); - assertEquals(getAllDeclaredTypes(testTypeElement), declaredTypes); - - // true true true false : self type + all super classes - declaredTypes = findDeclaredTypes(testTypeElement, true, true, true, false, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - - // true true false true : self type + all super interfaces - declaredTypes = findDeclaredTypes(testTypeElement, true, true, false, true, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - - // true true false false : self type - declaredTypes = findDeclaredTypes(testTypeElement, true, true, false, false, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SELF_TYPE); - - // true false true true : self type + super class + super interfaces - declaredTypes = findDeclaredTypes(testTypeElement, true, false, true, true, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - - // true false true false : self type + super class - declaredTypes = findDeclaredTypes(testTypeElement, true, false, true, false, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_CLASS); - - // true false false true : self type + super interfaces - declaredTypes = findDeclaredTypes(testTypeElement, true, false, false, true, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_INTERFACES); - - // true false false false : self type - declaredTypes = findDeclaredTypes(testTypeElement, true, false, false, false, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SELF_TYPE); - - // false true true true : all super types - declaredTypes = findDeclaredTypes(testTypeElement, false, true, true, true, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); - assertEquals(getAllDeclaredTypesOfSuperTypes(testTypeElement), declaredTypes); - - // false true true false : all super classes - declaredTypes = findDeclaredTypes(testTypeElement, false, true, true, false, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_SUPER_CLASSES); - assertEquals(getAllDeclaredTypesOfSuperclasses(testTypeElement), declaredTypes); - - // false true false true : all super interfaces - declaredTypes = findDeclaredTypes(testTypeElement, false, true, false, true, alwaysTrue()); - assertDeclaredTypes(declaredTypes, ALL_SUPER_INTERFACES); - assertEquals(getAllDeclaredTypesOfInterfaces(testTypeElement), declaredTypes); - - // false true false false : nothing - declaredTypes = findDeclaredTypes(testTypeElement, false, true, false, false, alwaysTrue()); - assertDeclaredTypes(declaredTypes); - assertEmptyList(declaredTypes); - - // false false true true : super class + super interfaces - declaredTypes = findDeclaredTypes(testTypeElement, false, false, true, true, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SUPER_TYPES); - - // false false true false : super class - declaredTypes = findDeclaredTypes(testTypeElement, false, false, true, false, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SUPER_CLASS); - assertEquals(ofList(getDeclaredTypeOfSuperclass(testTypeElement)), declaredTypes); - - // false false false true : super interfaces - declaredTypes = findDeclaredTypes(testTypeElement, false, false, false, true, alwaysTrue()); - assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); - assertEquals(ofList(getDeclaredTypesOfInterfaces(testTypeElement)), declaredTypes); - - // false false false false : nothing - declaredTypes = findDeclaredTypes(testTypeElement, false, false, false, false, alwaysTrue()); - assertDeclaredTypes(declaredTypes); - assertEmptyList(declaredTypes); - } - - @Test - void testFindDeclaredTypesOnNull() { - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, true, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, true, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, true, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, true, true, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, true, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, true, false, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, true, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, true, false, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, false, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, false, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, false, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, false, true, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, false, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, false, false, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, false, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, false, false, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, false, true, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, false, true, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, false, true, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, false, true, true, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, false, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, false, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, true, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, false, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, false, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, true, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, true, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, true, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, true, true, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, true, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, true, false, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, true, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, true, false, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, false, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, false, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, false, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, false, true, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, false, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, false, false, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, false, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, false, false, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, true, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, false, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, false, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, true, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, true, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, true, alwaysFalse())); - - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, false, alwaysFalse())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, false, alwaysTrue())); - assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, false, alwaysFalse())); - } - - @Test - void testGetTypeMirrorsOfInterfaces() { - List typeMirrors = getTypeMirrorsOfInterfaces(testTypeMirror); - assertTypeMirrors(typeMirrors, SUPER_INTERFACES); - - typeMirrors = getTypeMirrorsOfInterfaces(testTypeElement); - assertTypeMirrors(typeMirrors, SUPER_INTERFACES); - - typeMirrors = getTypeMirrorsOfInterfaces(getTypeElement(Object.class)); - assertEmptyList(typeMirrors); - - typeMirrors = getTypeMirrorsOfInterfaces(getTypeMirror(Object.class)); - assertSame(typeMirrors, typeMirrors); - } - - @Test - void testGetTypeMirrorsOfInterfacesOnNull() { - List typeMirrors = getTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR); - assertEmptyList(typeMirrors); - - typeMirrors = getTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT); - assertEmptyList(typeMirrors); - } - - @Test - void testFindTypeMirrorsOfInterfaces() { - List typeMirrors = findTypeMirrorsOfInterfaces(testTypeMirror, alwaysTrue()); - assertTypeMirrors(typeMirrors, SUPER_INTERFACES); - - typeMirrors = findTypeMirrorsOfInterfaces(testTypeElement, alwaysTrue()); - assertTypeMirrors(typeMirrors, SUPER_INTERFACES); - - typeMirrors = findTypeMirrorsOfInterfaces(testTypeMirror, alwaysFalse()); - assertEmptyList(typeMirrors); - - typeMirrors = findTypeMirrorsOfInterfaces(testTypeElement, alwaysFalse()); - assertEmptyList(typeMirrors); - - typeMirrors = findTypeMirrorsOfInterfaces(getTypeElement(Object.class), alwaysTrue()); - assertEmptyList(typeMirrors); - - typeMirrors = findTypeMirrorsOfInterfaces(getTypeElement(Object.class), alwaysFalse()); - assertEmptyList(typeMirrors); - - typeMirrors = findTypeMirrorsOfInterfaces(getTypeMirror(Object.class), alwaysTrue()); - assertSame(typeMirrors, typeMirrors); - - typeMirrors = findTypeMirrorsOfInterfaces(getTypeMirror(Object.class), alwaysFalse()); - assertSame(typeMirrors, typeMirrors); - } - - @Test - void testFindTypeMirrorsOfInterfacesOnNull() { - List typeMirrors = findTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue()); - assertEmptyList(typeMirrors); - - typeMirrors = findTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT, alwaysTrue()); - assertEmptyList(typeMirrors); - - typeMirrors = findTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysFalse()); - assertEmptyList(typeMirrors); - - typeMirrors = findTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT, alwaysFalse()); - assertEmptyList(typeMirrors); - } - - @Test - void testGetAllTypeMirrorsOfInterfaces() { - List typeMirrors = getAllTypeMirrorsOfInterfaces(testTypeMirror); - assertTypeMirrors(typeMirrors, ALL_SUPER_INTERFACES); - - typeMirrors = getAllTypeMirrorsOfInterfaces(testTypeElement); - assertTypeMirrors(typeMirrors, ALL_SUPER_INTERFACES); - - typeMirrors = getAllTypeMirrorsOfInterfaces(getTypeElement(Object.class)); - assertEmptyList(typeMirrors); - - typeMirrors = getAllTypeMirrorsOfInterfaces(getTypeMirror(Object.class)); - assertSame(typeMirrors, typeMirrors); - } - - @Test - void testGetAllTypeMirrorsOfInterfacesOnNull() { - List typeMirrors = getAllTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR); - assertEmptyList(typeMirrors); - - typeMirrors = getAllTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT); - assertEmptyList(typeMirrors); - } - - @Test - void testFindAllTypeMirrorsOfInterfaces() { - List typeMirrors = findAllTypeMirrorsOfInterfaces(testTypeMirror, alwaysTrue()); - assertTypeMirrors(typeMirrors, ALL_SUPER_INTERFACES); - - typeMirrors = findAllTypeMirrorsOfInterfaces(testTypeElement, alwaysTrue()); - assertTypeMirrors(typeMirrors, ALL_SUPER_INTERFACES); - - typeMirrors = findAllTypeMirrorsOfInterfaces(testTypeMirror, alwaysFalse()); - assertEmptyList(typeMirrors); - - typeMirrors = findAllTypeMirrorsOfInterfaces(testTypeElement, alwaysFalse()); - assertEmptyList(typeMirrors); - - typeMirrors = findAllTypeMirrorsOfInterfaces(getTypeElement(Object.class), alwaysTrue()); - assertEmptyList(typeMirrors); - - typeMirrors = findAllTypeMirrorsOfInterfaces(getTypeElement(Object.class), alwaysFalse()); - assertEmptyList(typeMirrors); - - typeMirrors = findAllTypeMirrorsOfInterfaces(getTypeMirror(Object.class), alwaysTrue()); - assertSame(typeMirrors, typeMirrors); - - typeMirrors = findAllTypeMirrorsOfInterfaces(getTypeMirror(Object.class), alwaysFalse()); - assertSame(typeMirrors, typeMirrors); - } - - @Test - void testFindAllTypeMirrorsOfInterfacesOnNull() { - List typeMirrors = findAllTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue()); - assertEmptyList(typeMirrors); - - typeMirrors = findAllTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysFalse()); - assertEmptyList(typeMirrors); - - typeMirrors = findAllTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT, alwaysTrue()); - assertEmptyList(typeMirrors); - - typeMirrors = findAllTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysFalse()); - assertEmptyList(typeMirrors); - } - - @Test - void testFindInterfaceTypeMirror() { - for (Type interfaceType : ALL_SUPER_INTERFACES) { - assertInterfaceTypeMirror(testTypeElement, interfaceType); - } - - for (Type superClass : ALL_SUPER_CLASSES) { - assertNull(findInterfaceTypeMirror(testTypeElement, superClass)); - assertNull(findInterfaceTypeMirror(testTypeMirror, superClass)); - } - } - - @Test - void testFindInterfaceTypeMirrorOnNull() { - for (Type type : ALL_TYPES) { - assertNull(findInterfaceTypeMirror(NULL_ELEMENT, type)); - assertNull(findInterfaceTypeMirror(NULL_TYPE_MIRROR, type)); - } - } - - @Test - void testGetTypeMirrors() { - assertGetTypeMirrors(NULL_TYPE); - assertGetTypeMirrors(SELF_TYPE); - assertGetTypeMirrors(SUPER_CLASS); - assertGetTypeMirrors(ALL_TYPES); - assertGetTypeMirrors(ALL_SUPER_TYPES); - assertGetTypeMirrors(ALL_SUPER_CLASSES); - assertGetTypeMirrors(ALL_SUPER_INTERFACES); - assertGetTypeMirrors(SUPER_INTERFACES); - assertGetTypeMirrors(SUPER_TYPES); - - assertGetTypeMirrors(SELF_TYPE_PLUS_ALL_SUPER_TYPES); - assertGetTypeMirrors(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - assertGetTypeMirrors(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - assertGetTypeMirrors(SELF_TYPE_PLUS_SUPER_CLASS); - assertGetTypeMirrors(SELF_TYPE_PLUS_SUPER_INTERFACES); - assertGetTypeMirrors(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testGetTypeMirrorsOnNull() { - assertGetTypeMirrors(NULL_TYPE); - assertGetTypeMirrors(NULL_TYPE_ARRAY); - assertGetTypeMirrors(EMPTY_TYPE_ARRAY); - - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SUPER_CLASS); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, ALL_TYPES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, ALL_SUPER_TYPES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, ALL_SUPER_CLASSES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, ALL_SUPER_INTERFACES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SUPER_INTERFACES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SUPER_TYPES); - - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_ALL_SUPER_TYPES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_SUPER_CLASS); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_SUPER_INTERFACES); - TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testGetTypeMirror() { - assertGetTypeMirror(NULL_TYPE); - assertGetTypeMirror(SELF_TYPE); - assertGetTypeMirror(SUPER_CLASS); - assertGetTypeMirror(ALL_TYPES); - assertGetTypeMirror(ALL_SUPER_TYPES); - assertGetTypeMirror(ALL_SUPER_CLASSES); - assertGetTypeMirror(ALL_SUPER_INTERFACES); - assertGetTypeMirror(SUPER_INTERFACES); - assertGetTypeMirror(SUPER_TYPES); - - assertGetTypeMirror(SELF_TYPE_PLUS_ALL_SUPER_TYPES); - assertGetTypeMirror(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - assertGetTypeMirror(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - assertGetTypeMirror(SELF_TYPE_PLUS_SUPER_CLASS); - assertGetTypeMirror(SELF_TYPE_PLUS_SUPER_INTERFACES); - assertGetTypeMirror(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testGetTypeMirrorOnNull() { - assertNull(TypeUtils.getTypeMirror(this.processingEnv, NULL_TYPE)); - assertNull(getTypeMirror(NULL_TYPE)); - - assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE); - assertGetTypeMirrorOnNullProcessingEnvironment(SUPER_CLASS); - assertGetTypeMirrorOnNullProcessingEnvironment(ALL_TYPES); - assertGetTypeMirrorOnNullProcessingEnvironment(ALL_SUPER_TYPES); - assertGetTypeMirrorOnNullProcessingEnvironment(ALL_SUPER_CLASSES); - assertGetTypeMirrorOnNullProcessingEnvironment(ALL_SUPER_INTERFACES); - assertGetTypeMirrorOnNullProcessingEnvironment(SUPER_INTERFACES); - assertGetTypeMirrorOnNullProcessingEnvironment(SUPER_TYPES); - - assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_TYPES); - assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS); - assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_INTERFACES); - assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testGetTypeElementsWithProcessingEnvironment() { - assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE); - assertGetTypeElementsWithProcessingEnvironment(SUPER_CLASS); - assertGetTypeElementsWithProcessingEnvironment(ALL_TYPES); - assertGetTypeElementsWithProcessingEnvironment(ALL_SUPER_TYPES); - assertGetTypeElementsWithProcessingEnvironment(ALL_SUPER_CLASSES); - assertGetTypeElementsWithProcessingEnvironment(ALL_SUPER_INTERFACES); - assertGetTypeElementsWithProcessingEnvironment(SUPER_INTERFACES); - assertGetTypeElementsWithProcessingEnvironment(SUPER_TYPES); - - assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_TYPES); - assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS); - assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_SUPER_INTERFACES); - assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testGetTypeElementsWithProcessingEnvironmentOnNull() { - assertEmptyList(TypeUtils.getTypeElements(this.processingEnv, NULL_TYPE)); - assertEmptyList(TypeUtils.getTypeElements(this.processingEnv, NULL_TYPE_ARRAY)); - assertEmptyList(TypeUtils.getTypeElements(this.processingEnv, EMPTY_TYPE_ARRAY)); - - assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE); - assertGetTypeElementsOnNullProcessingEnvironment(SUPER_CLASS); - assertGetTypeElementsOnNullProcessingEnvironment(ALL_TYPES); - assertGetTypeElementsOnNullProcessingEnvironment(ALL_SUPER_TYPES); - assertGetTypeElementsOnNullProcessingEnvironment(ALL_SUPER_CLASSES); - assertGetTypeElementsOnNullProcessingEnvironment(ALL_SUPER_INTERFACES); - assertGetTypeElementsOnNullProcessingEnvironment(SUPER_INTERFACES); - assertGetTypeElementsOnNullProcessingEnvironment(SUPER_TYPES); - - assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_TYPES); - assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS); - assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_INTERFACES); - assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testGetTypeElement() { - assertEquals(testTypeElement, TypeUtils.getTypeElement(processingEnv, testClassName)); - } - - @Test - void testGetTypeElementOnTypeMirror() { - assertEquals(testTypeElement, TypeUtils.getTypeElement(processingEnv, testTypeMirror)); - } - - @Test - void testGetTypeElementOnType() { - assertEquals(testTypeElement, TypeUtils.getTypeElement(processingEnv, SELF_TYPE)); - } - - @Test - void testGetTypeElementOnNull() { - assertNull(TypeUtils.getTypeElement(processingEnv, NULL_TYPE)); - assertNull(TypeUtils.getTypeElement(processingEnv, NULL_TYPE_MIRROR)); - assertNull(TypeUtils.getTypeElement(processingEnv, NULL_STRING)); - assertNull(TypeUtils.getTypeElement(NULL_PROCESSING_ENVIRONMENT, NULL_STRING)); - } - - @Test - void testGetDeclaredType() { - assertGetDeclaredType(NULL_TYPE); - assertGetDeclaredType(SELF_TYPE); - assertGetDeclaredType(SUPER_CLASS); - assertGetDeclaredType(ALL_TYPES); - assertGetDeclaredType(ALL_SUPER_TYPES); - assertGetDeclaredType(ALL_SUPER_CLASSES); - assertGetDeclaredType(ALL_SUPER_INTERFACES); - assertGetDeclaredType(SUPER_INTERFACES); - assertGetDeclaredType(SUPER_TYPES); - - assertGetDeclaredType(SELF_TYPE_PLUS_ALL_SUPER_TYPES); - assertGetDeclaredType(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - assertGetDeclaredType(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - assertGetDeclaredType(SELF_TYPE_PLUS_SUPER_CLASS); - assertGetDeclaredType(SELF_TYPE_PLUS_SUPER_INTERFACES); - assertGetDeclaredType(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testGetDeclaredTypeOnNull() { - assertNull(TypeUtils.getDeclaredType(this.processingEnv, NULL_TYPE)); - assertNull(TypeUtils.getDeclaredType(this.processingEnv, NULL_TYPE_MIRROR)); - assertNull(TypeUtils.getDeclaredType(this.processingEnv, NULL_STRING)); - - assertNull(getDeclaredType(NULL_TYPE)); - - assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE); - assertGetDeclaredTypeOnNullProcessingEnvironment(SUPER_CLASS); - assertGetDeclaredTypeOnNullProcessingEnvironment(ALL_TYPES); - assertGetDeclaredTypeOnNullProcessingEnvironment(ALL_SUPER_TYPES); - assertGetDeclaredTypeOnNullProcessingEnvironment(ALL_SUPER_CLASSES); - assertGetDeclaredTypeOnNullProcessingEnvironment(ALL_SUPER_INTERFACES); - assertGetDeclaredTypeOnNullProcessingEnvironment(SUPER_INTERFACES); - assertGetDeclaredTypeOnNullProcessingEnvironment(SUPER_TYPES); - - assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_TYPES); - assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS); - assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_INTERFACES); - assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - @Test - void testToStringOnClasses() { - assertToStringOnClasses(); - } - - @Test - void testToStringOnArrayTypes() { - assertToStringOnArrayTypes(); - } - - @Test - void testToStringOnCollectionTypes() { - assertToStringOnCollectionTypes(); - } - - @Test - void testToStringMapTypes() { - assertToStringOnMapTypes(); - } - - @Test - void testToStringOnNull() { - assertNull(TypeUtils.toString(NULL_TYPE_MIRROR)); - } - - @Test - void testTypeElementFinderOnNull() { - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, true, true, true)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, true, true, false)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, true, false, true)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, true, false, false)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, false, true, true)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, true, false)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, false, true)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, false, false)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, true, true, true)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, true, true, false)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, true, false, true)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, true, false, false)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, true, true)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, true, false)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, false, true)); - - assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, false, false)); - } - - private void assertIsSameType(Element typeElement, Type type) { - assertTrue(isSameType(typeElement, type)); - assertTrue(isSameType(typeElement, type.getTypeName())); - assertTrue(isSameType(typeElement.asType(), type)); - assertTrue(isSameType(typeElement.asType(), type.getTypeName())); - } - - private void assertOfTypeMirrors(Class... types) { - Element[] elements = getElements(types); - assertEquals(getTypeMirrors(types), ofTypeMirrors(elements)); - } - - private void assertOfTypeElements(Class... types) { - List typesList = getTypeMirrors(types); - List typeElements = ofTypeElements(typesList); - for (TypeMirror typeMirror : typesList) { - assertTrue(typeElements.contains(ofTypeElement(typeMirror))); - } - } - - private void assertOfDeclaredTypes(Class... types) { - Element[] elements = getElements(types); - List declaredTypes = ofDeclaredTypes(elements); - assertDeclaredTypes(declaredTypes, types); - } - - private void assertIsArrayType(Type type) { - assertTrue(isArrayType(getFieldType(type, "integers"))); - assertTrue(isArrayType(getFieldType(type, "strings"))); - assertTrue(isArrayType(getFieldType(type, "primitiveTypeModels"))); - assertTrue(isArrayType(getFieldType(type, "models"))); - assertTrue(isArrayType(getFieldType(type, "colors"))); - } - - private void assertIsArrayType(Element element) { - assertTrue(isArrayType(findField(element, "integers"))); - assertTrue(isArrayType(findField(element, "strings"))); - assertTrue(isArrayType(findField(element, "primitiveTypeModels"))); - assertTrue(isArrayType(findField(element, "models"))); - assertTrue(isArrayType(findField(element, "colors"))); - } - - private void assertTypeMirrors(List typeMirrors, Type... types) { - int length = length(types); - for (int i = 0; i < length; i++) { - if (types[i] == null) { - length--; - } - } - assertEquals(length, typeMirrors.size()); - for (int i = 0; i < length; i++) { - assertSame(typeMirrors.get(i), getTypeMirror(types[i])); - } - } - - private void assertTypeElements(List typeElements, Type... types) { - int length = length(types); - for (int i = 0; i < length; i++) { - if (types[i] == null) { - length--; - } - } - assertEquals(length, typeElements.size()); - for (int i = 0; i < length; i++) { - assertSame(typeElements.get(i), getTypeElement(types[i])); - } - } - - private void assertDeclaredTypes(List declaredTypes, Type... types) { - int length = length(types); - for (int i = 0; i < length; i++) { - if (types[i] == null) { - length--; - } - } - assertEquals(length, declaredTypes.size()); - for (int i = 0; i < length; i++) { - assertSame(declaredTypes.get(i), getDeclaredType(types[i])); - } - } - - private void assertInterfaceTypeMirror(Element type, Type interfaceType) { - TypeMirror typeMirror = findInterfaceTypeMirror(type, interfaceType); - assertTrue(isSameType(typeMirror, interfaceType)); - - typeMirror = findInterfaceTypeMirror(type.asType(), interfaceType); - assertTrue(isSameType(typeMirror, interfaceType)); - } - - private void assertGetTypeMirrors(Type... types) { - List typeMirrors = TypeUtils.getTypeMirrors(processingEnv, types); - assertEquals(typeMirrors, getTypeMirrors(types)); - assertTypeMirrors(typeMirrors, types); - } - - private void assertGetTypeMirror(Type... types) { - int length = length(types); - for (int i = 0; i < length; i++) { - Type type = types[i]; - TypeMirror typeMirror = TypeUtils.getTypeMirror(processingEnv, type); - assertSame(getTypeMirror(type), typeMirror); - assertTrue(isSameType(typeMirror, type)); - } - } - - private void assertGetTypeMirrorOnNullProcessingEnvironment(Type... types) { - int length = length(types); - for (int i = 0; i < length; i++) { - Type type = types[i]; - TypeMirror typeMirror = TypeUtils.getTypeMirror(NULL_PROCESSING_ENVIRONMENT, type); - assertNull(typeMirror); - } - } - - private void assertGetTypeElementsWithProcessingEnvironment(Type... types) { - List typeElements = TypeUtils.getTypeElements(this.processingEnv, types); - assertEquals(getTypeElements(types), typeElements); - assertTypeElements(typeElements, types); - } - - private void assertGetTypeElementsOnNullProcessingEnvironment(Type... types) { - List typeElements = TypeUtils.getTypeElements(NULL_PROCESSING_ENVIRONMENT, types); - assertEmptyList(typeElements); - } - - private void assertGetDeclaredType(Type... types) { - int length = length(types); - for (int i = 0; i < length; i++) { - Type type = types[i]; - DeclaredType declaredType = TypeUtils.getDeclaredType(processingEnv, type); - assertSame(getDeclaredType(type), declaredType); - assertSame(getDeclaredType(type), TypeUtils.getDeclaredType(processingEnv, declaredType)); - assertTrue(isSameType(declaredType, type)); - } - } - - private void assertGetDeclaredTypeOnNullProcessingEnvironment(Type... types) { - int length = length(types); - for (int i = 0; i < length; i++) { - Type type = types[i]; - TypeMirror typeMirror = TypeUtils.getDeclaredType(NULL_PROCESSING_ENVIRONMENT, type); - assertNull(typeMirror); - } - } - - private void assertToStringOnMapTypes() { - assertToString(getFieldType(MapTypeModel.class, "strings")); - assertToString(getFieldType(MapTypeModel.class, "colors")); - assertToString(getFieldType(MapTypeModel.class, "primitiveTypeModels")); - assertToString(getFieldType(MapTypeModel.class, "models")); - assertToString(getFieldType(MapTypeModel.class, "modelArrays")); - } - - private void assertToStringOnCollectionTypes() { - assertToString(getFieldType(CollectionTypeModel.class, "strings")); - assertToString(getFieldType(CollectionTypeModel.class, "colors")); - assertToString(getFieldType(CollectionTypeModel.class, "primitiveTypeModels")); - assertToString(getFieldType(CollectionTypeModel.class, "models")); - assertToString(getFieldType(CollectionTypeModel.class, "modelArrays")); - } - - private void assertToStringOnArrayTypes() { - assertToString(getFieldType(ArrayTypeModel.class, "integers")); - assertToString(getFieldType(ArrayTypeModel.class, "strings")); - assertToString(getFieldType(ArrayTypeModel.class, "primitiveTypeModels")); - assertToString(getFieldType(ArrayTypeModel.class, "models")); - assertToString(getFieldType(ArrayTypeModel.class, "colors")); - } - - private TypeMirror getFieldType(Type type, String fieldName) { - TypeMirror typeMirror = getTypeMirror(type); - return findField(typeMirror, fieldName).asType(); - } - - private TypeMirror getFieldType(Element element, String fieldName) { - return findField(element, fieldName).asType(); - } - - private void assertToStringOnClasses() { - assertToString(NULL_TYPE); - assertToString(SELF_TYPE); - assertToString(SUPER_CLASS); - assertToString(ALL_TYPES); - assertToString(ALL_SUPER_TYPES); - assertToString(ALL_SUPER_CLASSES); - assertToString(ALL_SUPER_INTERFACES); - assertToString(SUPER_INTERFACES); - assertToString(SUPER_TYPES); - - assertToString(SELF_TYPE_PLUS_ALL_SUPER_TYPES); - assertToString(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); - assertToString(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); - assertToString(SELF_TYPE_PLUS_SUPER_CLASS); - assertToString(SELF_TYPE_PLUS_SUPER_INTERFACES); - assertToString(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); - } - - private void assertToString(Type... types) { - int length = length(types); - for (int i = 0; i < length; i++) { - if (types[i] == null) { - length--; - } - } - for (int i = 0; i < length; i++) { - TypeMirror typeMirror = getTypeMirror(types[i]); - assertEquals(types[i].getTypeName(), TypeUtils.toString(typeMirror)); - } - } - - private void assertToString(TypeMirror type) { - assertEquals(type.toString(), TypeUtils.toString(type)); - } -} \ No newline at end of file From 3404443afcaa6cdd09448c4a15704509d0e63e32 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:04:24 +0800 Subject: [PATCH 021/438] Add microsphere-lang-model module Introduce a new microsphere-lang-model Maven module. Adds pom.xml with dependencies and build configuration, core language-model classes (e.g. StringAnnotationValue) and a comprehensive set of utility classes for annotation/element processing (AnnotationUtils, ClassUtils, ConstructorUtils, ElementUtils, ExecutableElementComparator, FieldUtils, MethodUtils, TypeUtils, Resolvable/JSON visitors, LoggerUtils, MessagerUtils, MemberUtils, etc.). Includes unit tests for the added utilities and elements to validate behavior. This module provides reusable helpers for working with javax.lang.model types and annotation processing within the Microsphere project. --- microsphere-lang-model/pom.xml | 133 + .../model/element/StringAnnotationValue.java | 50 + .../lang/model/util/AnnotationUtils.java | 1870 ++++++++++++ .../lang/model/util/ClassUtils.java | 109 + .../lang/model/util/ConstructorUtils.java | 293 ++ .../lang/model/util/ElementUtils.java | 734 +++++ .../util/ExecutableElementComparator.java | 93 + .../lang/model/util/FieldUtils.java | 763 +++++ .../util/JSONAnnotationValueVisitor.java | 196 ++ .../lang/model/util/JSONElementVisitor.java | 221 ++ .../lang/model/util/LoggerUtils.java | 54 + .../lang/model/util/MemberUtils.java | 423 +++ .../lang/model/util/MessagerUtils.java | 310 ++ .../lang/model/util/MethodUtils.java | 1165 ++++++++ .../ResolvableAnnotationValueVisitor.java | 200 ++ .../lang/model/util/TypeUtils.java | 2505 +++++++++++++++++ .../element/StringAnnotationValueTest.java | 53 + .../lang/model/util/AnnotationUtilsTest.java | 775 +++++ .../lang/model/util/ClassUtilsTest.java | 51 + .../lang/model/util/ConstructorUtilsTest.java | 150 + .../lang/model/util/ElementUtilsTest.java | 351 +++ .../util/ExecutableElementComparatorTest.java | 76 + .../lang/model/util/FieldUtilsTest.java | 412 +++ .../util/JSONAnnotationValueVisitorTest.java | 135 + .../model/util/JSONElementVisitorTest.java | 254 ++ .../lang/model/util/LoggerUtilsTest.java | 76 + .../lang/model/util/MemberUtilsTest.java | 173 ++ .../lang/model/util/MessagerUtilsTest.java | 81 + .../lang/model/util/MethodUtilsTest.java | 507 ++++ .../ResolvableAnnotationValueVisitorTest.java | 216 ++ .../lang/model/util/TypeUtilsTest.java | 1876 ++++++++++++ .../microsphere/lang/model/util/UtilTest.java | 88 + 32 files changed, 14393 insertions(+) create mode 100644 microsphere-lang-model/pom.xml create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/element/StringAnnotationValue.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ClassUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ConstructorUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ElementUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ExecutableElementComparator.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/FieldUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitor.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/LoggerUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MemberUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MessagerUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java create mode 100644 microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/element/StringAnnotationValueTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ClassUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ConstructorUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ExecutableElementComparatorTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/FieldUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/LoggerUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MemberUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/TypeUtilsTest.java create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java diff --git a/microsphere-lang-model/pom.xml b/microsphere-lang-model/pom.xml new file mode 100644 index 000000000..2c674b268 --- /dev/null +++ b/microsphere-lang-model/pom.xml @@ -0,0 +1,133 @@ + + + + io.github.microsphere-projects + microsphere-java-parent + ${revision} + ../microsphere-java-parent/pom.xml + + 4.0.0 + + io.github.microsphere-projects + microsphere-lang-model + ${revision} + jar + + Microsphere :: Java :: Language Model + Microsphere Language Model + + + 2.1 + 2.3.1 + + + + + + + io.github.microsphere-projects + microsphere-java-core + ${revision} + + + + + org.junit.jupiter + junit-jupiter + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + io.github.microsphere-projects + microsphere-jdk-tools + ${revision} + test + + + + + io.github.microsphere-projects + microsphere-java-test + ${revision} + test + + + + + ch.qos.logback + logback-classic + test + + + + + javax.ws.rs + javax.ws.rs-api + ${javax.ws.rs.version} + test + + + + + javax.xml.ws + jaxws-api + ${jaxws-api.version} + test + + + + + org.springframework + spring-context + test + + + + org.springframework + spring-web + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + -proc:none + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + io.github.microsphere-projects:microsphere-java-core + + + + + + + + + + \ No newline at end of file diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/element/StringAnnotationValue.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/element/StringAnnotationValue.java new file mode 100644 index 000000000..ba92765e6 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/element/StringAnnotationValue.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.element; + +import io.microsphere.annotation.Immutable; + +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.AnnotationValueVisitor; + +/** + * {@link AnnotationValue} for String type + * + * @author Mercy + * @see AnnotationValue + * @since 1.0.0 + */ +@Immutable +public class StringAnnotationValue implements AnnotationValue { + + private final String value; + + public StringAnnotationValue(String value) { + this.value = value; + } + + @Override + public Object getValue() { + return this.value; + } + + @Override + public R accept(AnnotationValueVisitor v, P p) { + return v.visitString(this.value, p); + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java new file mode 100644 index 000000000..bbfce4988 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java @@ -0,0 +1,1870 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.annotation.Immutable; +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.util.Utils; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.AnnotatedConstruct; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.AnnotationValueVisitor; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.function.Predicate; + +import static io.microsphere.lang.model.util.MethodUtils.findDeclaredMethods; +import static io.microsphere.lang.model.util.MethodUtils.getDeclaredMethods; +import static io.microsphere.lang.model.util.MethodUtils.getMethodName; +import static io.microsphere.lang.model.util.TypeUtils.getAllTypeElements; +import static io.microsphere.lang.model.util.TypeUtils.getTypeElement; +import static io.microsphere.lang.model.util.TypeUtils.isSameType; +import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.collection.CollectionUtils.size; +import static io.microsphere.collection.MapUtils.immutableEntry; +import static io.microsphere.collection.MapUtils.isEmpty; +import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static io.microsphere.lang.function.Streams.filterAll; +import static io.microsphere.util.ArrayUtils.isNotEmpty; +import static io.microsphere.util.StringUtils.isBlank; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableMap; +import static java.util.stream.Collectors.toList; + +/** + * A utility interface for working with annotations in the {@code javax.lang.model.*} package. + *

+ * This interface provides methods to retrieve, filter, and check annotations and their attributes + * from various constructs such as {@link Element}, {@link TypeMirror}, and others commonly used + * in annotation processing. + *

+ * + *

Example Usage

+ *
{@code
+ * // Retrieve a specific annotation from an element
+ * AnnotationMirror annotation = AnnotationUtils.getAnnotation(element, MyAnnotation.class);
+ *
+ * // Retrieve all annotations from a type
+ * List annotations = AnnotationUtils.getAnnotations(typeMirror);
+ *
+ * // Check if an annotation is present on an element
+ * boolean present = AnnotationUtils.isAnnotationPresent(element, MyAnnotation.class);
+ *
+ * // Find meta-annotations on an annotation
+ * AnnotationMirror metaAnnotation = AnnotationUtils.findMetaAnnotation(annotation, MyMetaAnnotation.class);
+ * }
+ * + *

+ * This interface is intended to be implemented by classes that provide static utility methods + * for handling annotations during annotation processing. It helps in organizing and reusing + * common operations related to annotations. + *

+ * + * @author Mercy + * @since 1.0.0 + */ +public interface AnnotationUtils extends Utils { + + /** + * The name of the attribute method : value() + */ + String VALUE_ATTRIBUTE_NAME = "value"; + + /** + * The default {@link AnnotationValueVisitor} + */ + AnnotationValueVisitor DEFAULT_ANNOTATION_VALUE_VISITOR = new ResolvableAnnotationValueVisitor(); + + /** + * The empty {@link ElementType} array + */ + @Immutable + ElementType[] EMPTY_ELEMENT_TYPE_ARRAY = new ElementType[0]; + + boolean WITH_DEFAULT = true; + + /** + * Retrieves the first {@link AnnotationMirror} of the specified annotation class from the given + * {@link AnnotatedConstruct}. If either the construct or the annotation class is {@code null}, + * this method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * // Get an annotation directly present on a class
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * AnnotationMirror annotation = AnnotationUtils.getAnnotation(typeElement, MyAnnotation.class);
+     *
+     * // Get an annotation from an element that may inherit annotations from its parent
+     * Element element = ...; // obtain an Element
+     * annotation = AnnotationUtils.getAnnotation(element, MyAnnotation.class);
+     * }
+ * + * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} + * @param annotationClass the annotation class to look for, may be {@code null} + * @return the first matching {@link AnnotationMirror}, or {@code null} if none found + */ + static AnnotationMirror getAnnotation(AnnotatedConstruct annotatedConstruct, Class annotationClass) { + if (annotatedConstruct == null || annotationClass == null) { + return null; + } + return getAnnotation(annotatedConstruct, annotationClass.getName()); + } + + /** + * Retrieves the first {@link AnnotationMirror} of the specified annotation class name from the given + * {@link AnnotatedConstruct}. If either the construct or the annotation class name is {@code null}, + * this method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * // Get an annotation by its class name from an element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * String annotationClassName = "com.example.MyAnnotation";
+     * AnnotationMirror annotation = AnnotationUtils.getAnnotation(typeElement, annotationClassName);
+     *
+     * // Get an annotation from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotation = AnnotationUtils.getAnnotation(methodElement, "com.example.AnotherAnnotation");
+     * }
+ * + * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} + * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} + * @return the first matching {@link AnnotationMirror}, or {@code null} if none found + */ + static AnnotationMirror getAnnotation(AnnotatedConstruct annotatedConstruct, CharSequence annotationClassName) { + if (annotatedConstruct == null || annotationClassName == null) { + return null; + } + List annotations = getAnnotations(annotatedConstruct, annotationClassName); + return annotations.isEmpty() ? null : annotations.get(0); + } + + /** + * Retrieves all {@link AnnotationMirror} instances from the given {@link AnnotatedConstruct}. + * If the annotated construct is {@code null}, this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve all annotations from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * List annotations = AnnotationUtils.getAnnotations(typeElement);
+     *
+     * // Retrieve annotations from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotations = AnnotationUtils.getAnnotations(methodElement);
+     * }
+ * + * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAnnotations(AnnotatedConstruct annotatedConstruct) { + if (annotatedConstruct == null) { + return emptyList(); + } + return findAnnotations(annotatedConstruct, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotation class from the given + * {@link AnnotatedConstruct}. If either the construct or the annotation class is {@code null}, + * this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve all annotations of a specific type from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * List annotations = AnnotationUtils.getAnnotations(typeElement, MyAnnotation.class);
+     *
+     * // Retrieve annotations from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotations = AnnotationUtils.getAnnotations(methodElement, AnotherAnnotation.class);
+     * }
+ * + * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} + * @param annotationClass the annotation class to look for, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAnnotations(AnnotatedConstruct annotatedConstruct, Class annotationClass) { + if (annotatedConstruct == null || annotationClass == null) { + return emptyList(); + } + return getAnnotations(annotatedConstruct, annotationClass.getTypeName()); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotation class name from the given + * {@link AnnotatedConstruct}. If either the construct or the annotation class name is {@code null}, + * this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve annotations by their class name from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * String annotationClassName = "com.example.MyAnnotation";
+     * List annotations = AnnotationUtils.getAnnotations(typeElement, annotationClassName);
+     *
+     * // Retrieve annotations from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotations = AnnotationUtils.getAnnotations(methodElement, "com.example.AnotherAnnotation");
+     * }
+ * + * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} + * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAnnotations(AnnotatedConstruct annotatedConstruct, CharSequence annotationClassName) { + if (annotatedConstruct == null || annotationClassName == null) { + return emptyList(); + } + return findAnnotations(annotatedConstruct, annotation -> matchesAnnotationTypeName(annotation, annotationClassName)); + } + + /** + * Retrieves all {@link AnnotationMirror} instances from the given {@link TypeMirror}. + * If the type mirror is {@code null}, this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve all annotations from a type mirror
+     * TypeMirror type = ...; // obtain a TypeMirror
+     * List annotations = AnnotationUtils.getAllAnnotations(type);
+     *
+     * // Handle cases where the type might be null
+     * annotations = AnnotationUtils.getAllAnnotations(null); // returns empty list
+     * }
+ * + * @param type the type mirror to search for annotations, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAllAnnotations(TypeMirror type) { + if (type == null) { + return emptyList(); + } + return getAllAnnotations(ofTypeElement(type)); + } + + /** + * Retrieves all {@link AnnotationMirror} instances from the given {@link Element}. + * If the element is {@code null}, this method returns an empty list. + * + *

This method is designed to provide a consistent way of retrieving annotations + * across different constructs in the annotation processing framework.

+ * + *

Example Usage

+ *
{@code
+     * // Retrieve all annotations from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * List annotations = AnnotationUtils.getAllAnnotations(typeElement);
+     *
+     * // Retrieve annotations from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotations = AnnotationUtils.getAllAnnotations(methodElement);
+     *
+     * // Handle cases where the element might be null
+     * annotations = AnnotationUtils.getAllAnnotations(null); // returns empty list
+     * }
+ * + * @param element the annotated element to search for annotations, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAllAnnotations(Element element) { + if (element == null) { + return emptyList(); + } + return findAllAnnotations(element, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotation class from the given + * {@link TypeMirror}. If either the type or the annotation class is {@code null}, this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve annotations from a type mirror
+     * TypeMirror type = ...; // obtain a TypeMirror
+     * List annotations = AnnotationUtils.getAllAnnotations(type, MyAnnotation.class);
+     *
+     * // Handle cases where the type might be null
+     * annotations = AnnotationUtils.getAllAnnotations(null, MyAnnotation.class); // returns empty list
+     *
+     * // Handle cases where the annotation class might be null
+     * annotations = AnnotationUtils.getAllAnnotations(type, null); // returns empty list
+     * }
+ * + * @param type the type mirror to search for annotations, may be {@code null} + * @param annotationClass the annotation class to look for, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAllAnnotations(TypeMirror type, Class annotationClass) { + if (type == null || annotationClass == null) { + return emptyList(); + } + return getAllAnnotations(ofTypeElement(type), annotationClass); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotation class from the given + * {@link Element}. If either the element or the annotation class is {@code null}, + * this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve annotations from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * List annotations = AnnotationUtils.getAllAnnotations(typeElement, MyAnnotation.class);
+     *
+     * // Retrieve annotations from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotations = AnnotationUtils.getAllAnnotations(methodElement, AnotherAnnotation.class);
+     *
+     * // Handle cases where the element might be null
+     * annotations = AnnotationUtils.getAllAnnotations(null, MyAnnotation.class); // returns empty list
+     *
+     * // Handle cases where the annotation class might be null
+     * annotations = AnnotationUtils.getAllAnnotations(typeElement, null); // returns empty list
+     * }
+ * + * @param element the annotated element to search for annotations, may be {@code null} + * @param annotationClass the annotation class to look for, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAllAnnotations(Element element, Class annotationClass) { + if (element == null || annotationClass == null) { + return emptyList(); + } + return getAllAnnotations(element, annotationClass.getTypeName()); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotation class name from the given + * {@link TypeMirror}. If either the type or the annotation class name is {@code null}, + * this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve annotations from a type mirror
+     * TypeMirror type = ...; // obtain a TypeMirror
+     * String annotationClassName = "com.example.MyAnnotation";
+     * List annotations = AnnotationUtils.getAllAnnotations(type, annotationClassName);
+     *
+     * // Handle cases where the type might be null
+     * annotations = AnnotationUtils.getAllAnnotations(null, annotationClassName); // returns empty list
+     *
+     * // Handle cases where the annotation class name might be null
+     * annotations = AnnotationUtils.getAllAnnotations(type, null); // returns empty list
+     * }
+ * + * @param type the type mirror to search for annotations, may be {@code null} + * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAllAnnotations(TypeMirror type, CharSequence annotationClassName) { + if (type == null || annotationClassName == null) { + return emptyList(); + } + return getAllAnnotations(ofTypeElement(type), annotationClassName); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotation class name from the given + * {@link Element}. If either the element or the annotation class name is {@code null}, + * this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve annotations by their class name from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * String annotationClassName = "com.example.MyAnnotation";
+     * List annotations = AnnotationUtils.getAllAnnotations(typeElement, annotationClassName);
+     *
+     * // Retrieve annotations from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotations = AnnotationUtils.getAllAnnotations(methodElement, "com.example.AnotherAnnotation");
+     *
+     * // Handle cases where the element might be null
+     * annotations = AnnotationUtils.getAllAnnotations(null, annotationClassName); // returns empty list
+     *
+     * // Handle cases where the annotation class name might be null
+     * annotations = AnnotationUtils.getAllAnnotations(typeElement, null); // returns empty list
+     * }
+ * + * @param element the annotated element to search for annotations, may be {@code null} + * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAllAnnotations(Element element, CharSequence annotationClassName) { + if (element == null || annotationClassName == null) { + return emptyList(); + } + return findAllAnnotations(element, annotation -> matchesAnnotationTypeName(annotation, annotationClassName)); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotated type from the given + * {@link ProcessingEnvironment}. If either the processing environment or the annotated type is {@code null}, + * this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * // Retrieve annotations for a specific type from the processing environment
+     * ProcessingEnvironment processingEnv = ...; // obtain a ProcessingEnvironment instance
+     * Type annotatedType = MyAnnotation.class; // specify the annotation type
+     * List annotations = AnnotationUtils.getAllAnnotations(processingEnv, annotatedType);
+     *
+     * // Handle cases where the processing environment might be null
+     * annotations = AnnotationUtils.getAllAnnotations(null, annotatedType); // returns empty list
+     *
+     * // Handle cases where the annotated type might be null
+     * annotations = AnnotationUtils.getAllAnnotations(processingEnv, null); // returns empty list
+     * }
+ * + * @param processingEnv the processing environment used to retrieve annotations, may be {@code null} + * @param annotatedType the annotated type to search for annotations, may be {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List getAllAnnotations(ProcessingEnvironment processingEnv, Type annotatedType) { + if (processingEnv == null || annotatedType == null) { + return emptyList(); + } + return findAllAnnotations(processingEnv, annotatedType, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves the first {@link AnnotationMirror} of the specified annotation class from the given + * {@link TypeMirror}. If either the type or the annotation class is {@code null}, + * this method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * // Retrieve an annotation from a type mirror
+     * TypeMirror type = ...; // obtain a TypeMirror instance
+     * AnnotationMirror annotation = AnnotationUtils.findAnnotation(type, MyAnnotation.class);
+     *
+     * // Handle cases where the type might be null
+     * annotation = AnnotationUtils.findAnnotation(null, MyAnnotation.class); // returns null
+     *
+     * // Handle cases where the annotation class might be null
+     * annotation = AnnotationUtils.findAnnotation(type, null); // returns null
+     * }
+ * + * @param type the type mirror to search for annotations, may be {@code null} + * @param annotationClass the annotation class to look for, may be {@code null} + * @return the first matching {@link AnnotationMirror}, or {@code null} if none found + */ + static AnnotationMirror findAnnotation(TypeMirror type, Class annotationClass) { + if (type == null || annotationClass == null) { + return null; + } + return findAnnotation(type, annotationClass.getTypeName()); + } + + /** + * Retrieves the first {@link AnnotationMirror} of the specified annotation class name from the given + * {@link TypeMirror}. If either the type or the annotation class name is {@code null}, + * this method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * // Retrieve an annotation by its class name from a type mirror
+     * TypeMirror type = ...; // obtain a TypeMirror instance
+     * String annotationClassName = "com.example.MyAnnotation";
+     * AnnotationMirror annotation = AnnotationUtils.findAnnotation(type, annotationClassName);
+     *
+     * // Handle cases where the type might be null
+     * annotation = AnnotationUtils.findAnnotation(null, annotationClassName); // returns null
+     *
+     * // Handle cases where the annotation class name might be null
+     * annotation = AnnotationUtils.findAnnotation(type, null); // returns null
+     * }
+ * + * @param type the type mirror to search for annotations, may be {@code null} + * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} + * @return the first matching {@link AnnotationMirror}, or {@code null} if none found + */ + static AnnotationMirror findAnnotation(TypeMirror type, CharSequence annotationClassName) { + if (type == null || annotationClassName == null) { + return null; + } + return findAnnotation(ofTypeElement(type), annotationClassName); + } + + /** + * Retrieves the first {@link AnnotationMirror} of the specified annotation class from the given + * {@link Element}. If either the element or the annotation class is {@code null}, + * this method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * // Retrieve an annotation from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * AnnotationMirror annotation = AnnotationUtils.findAnnotation(typeElement, MyAnnotation.class);
+     *
+     * // Retrieve an annotation from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotation = AnnotationUtils.findAnnotation(methodElement, AnotherAnnotation.class);
+     *
+     * // Handle cases where the element might be null
+     * annotation = AnnotationUtils.findAnnotation(null, MyAnnotation.class); // returns null
+     *
+     * // Handle cases where the annotation class might be null
+     * annotation = AnnotationUtils.findAnnotation(typeElement, null); // returns null
+     * }
+ * + * @param element the annotated element to search for annotations, may be {@code null} + * @param annotationClass the annotation class to look for, may be {@code null} + * @return the first matching {@link AnnotationMirror}, or {@code null} if none found + */ + static AnnotationMirror findAnnotation(Element element, Class annotationClass) { + if (element == null || annotationClass == null) { + return null; + } + return findAnnotation(element, annotationClass.getTypeName()); + } + + /** + * Retrieves the first {@link AnnotationMirror} of the specified annotation class name from the given + * {@link Element}. If either the element or the annotation class name is {@code null}, + * this method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * // Retrieve an annotation by its class name from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * String annotationClassName = "com.example.MyAnnotation";
+     * AnnotationMirror annotation = AnnotationUtils.findAnnotation(typeElement, annotationClassName);
+     *
+     * // Retrieve an annotation from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * annotation = AnnotationUtils.findAnnotation(methodElement, "com.example.AnotherAnnotation");
+     *
+     * // Handle cases where the element might be null
+     * annotation = AnnotationUtils.findAnnotation(null, annotationClassName); // returns null
+     *
+     * // Handle cases where the annotation class name might be null
+     * annotation = AnnotationUtils.findAnnotation(typeElement, null); // returns null
+     * }
+ * + * @param element the annotated element to search for annotations, may be {@code null} + * @param annotationClassName the fully qualified class name of the annotation to look for, may be {@code null} + * @return the first matching {@link AnnotationMirror}, or {@code null} if none found + */ + static AnnotationMirror findAnnotation(Element element, CharSequence annotationClassName) { + if (element == null || annotationClassName == null) { + return null; + } + List annotations = findAllAnnotations(element, annotation -> matchesAnnotationTypeName(annotation, annotationClassName)); + return isEmpty(annotations) ? null : annotations.get(0); + } + + /** + * Retrieves the first meta-annotation of the specified meta-annotation class from the given annotated element. + * A meta-annotation is an annotation that is present on another annotation. If either the annotated element + * or the meta-annotation class is {@code null}, this method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * // Retrieve a meta-annotation from an annotation on a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * AnnotationMirror metaAnnotation = AnnotationUtils.findMetaAnnotation(typeElement, MyMetaAnnotation.class);
+     *
+     * // Retrieve a meta-annotation from an annotation on a method element
+     * Element methodElement = ...; // obtain a method Element
+     * metaAnnotation = AnnotationUtils.findMetaAnnotation(methodElement, AnotherMetaAnnotation.class);
+     *
+     * // Handle cases where the element might be null
+     * metaAnnotation = AnnotationUtils.findMetaAnnotation(null, MyMetaAnnotation.class); // returns null
+     *
+     * // Handle cases where the meta-annotation class might be null
+     * metaAnnotation = AnnotationUtils.findMetaAnnotation(typeElement, null); // returns null
+     * }
+ * + * @param annotatedConstruct the annotated element to search for meta-annotations, may be {@code null} + * @param metaAnnotationClass the annotation class to look for as a meta-annotation, may be {@code null} + * @return the first matching meta-{@link AnnotationMirror}, or {@code null} if none found + */ + static AnnotationMirror findMetaAnnotation(Element annotatedConstruct, Class metaAnnotationClass) { + if (annotatedConstruct == null || metaAnnotationClass == null) { + return null; + } + return findMetaAnnotation(annotatedConstruct, metaAnnotationClass.getName()); + } + + /** + * Retrieves the first meta-annotation of the specified meta-annotation class name from the given annotated element. + * A meta-annotation is an annotation that is present on another annotation. If either the annotated element + * or the meta-annotation class name is {@code null}, this method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * // Retrieve a meta-annotation by its class name from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * String metaAnnotationClassName = "com.example.MyMetaAnnotation";
+     * AnnotationMirror metaAnnotation = AnnotationUtils.findMetaAnnotation(typeElement, metaAnnotationClassName);
+     *
+     * // Retrieve a meta-annotation from a method element
+     * Element methodElement = ...; // obtain a method Element
+     * metaAnnotation = AnnotationUtils.findMetaAnnotation(methodElement, "com.example.AnotherMetaAnnotation");
+     *
+     * // Handle cases where the element might be null
+     * metaAnnotation = AnnotationUtils.findMetaAnnotation(null, metaAnnotationClassName); // returns null
+     *
+     * // Handle cases where the meta-annotation class name might be null
+     * metaAnnotation = AnnotationUtils.findMetaAnnotation(typeElement, null); // returns null
+     * }
+ * + * @param annotatedConstruct the annotated element to search for meta-annotations, may be {@code null} + * @param metaAnnotationClassName the fully qualified class name of the meta-annotation to look for, may be {@code null} + * @return the first matching meta-{@link AnnotationMirror}, or {@code null} if none found + */ + static AnnotationMirror findMetaAnnotation(Element annotatedConstruct, CharSequence metaAnnotationClassName) { + if (annotatedConstruct == null || metaAnnotationClassName == null) { + return null; + } + + AnnotationMirror metaAnnotation = null; + + List annotations = getAllAnnotations(annotatedConstruct); + int size = size(annotations); + + for (int i = 0; i < size; i++) { + AnnotationMirror annotation = annotations.get(i); + if ((metaAnnotation = findAnnotation(annotation.getAnnotationType(), metaAnnotationClassName)) != null) { + break; + } + } + + return metaAnnotation; + } + + /** + * Checks if the specified annotation is present on the given element, either directly or as a meta-annotation. + * + *

Example Usage

+ *
{@code
+     * // Check if an annotation is present on a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * boolean present = AnnotationUtils.isAnnotationPresent(typeElement, MyAnnotation.class);
+     *
+     * // Check if an annotation is present on a method element
+     * Element methodElement = ...; // obtain a method Element
+     * present = AnnotationUtils.isAnnotationPresent(methodElement, AnotherAnnotation.class);
+     *
+     * // Handle cases where the element might be null
+     * present = AnnotationUtils.isAnnotationPresent(null, MyAnnotation.class); // returns false
+     *
+     * // Handle cases where the annotation class might be null
+     * present = AnnotationUtils.isAnnotationPresent(typeElement, null); // returns false
+     * }
+ * + * @param element the element to check for the presence of the annotation; may be {@code null} + * @param annotationClass the annotation class to look for; may be {@code null} + * @return {@code true} if the annotation is present (either directly or as a meta-annotation), + * {@code false} otherwise or if either parameter is {@code null} + */ + static boolean isAnnotationPresent(Element element, Class annotationClass) { + if (element == null || annotationClass == null) { + return false; + } + return findAnnotation(element, annotationClass) != null || findMetaAnnotation(element, annotationClass) != null; + } + + /** + * Checks if the specified annotation (by class name) is present on the given element, either directly or as a meta-annotation. + * + *

Example Usage

+ *
{@code
+     * // Check if an annotation is present on a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * String annotationClassName = "com.example.MyAnnotation";
+     * boolean present = AnnotationUtils.isAnnotationPresent(typeElement, annotationClassName);
+     *
+     * // Check if an annotation is present on a method element
+     * Element methodElement = ...; // obtain a method Element
+     * present = AnnotationUtils.isAnnotationPresent(methodElement, "com.example.AnotherAnnotation");
+     *
+     * // Handle cases where the element might be null
+     * present = AnnotationUtils.isAnnotationPresent(null, annotationClassName); // returns false
+     *
+     * // Handle cases where the annotation class name might be null
+     * present = AnnotationUtils.isAnnotationPresent(typeElement, null); // returns false
+     * }
+ * + * @param element the element to check for the presence of the annotation; may be {@code null} + * @param annotationClassName the fully qualified class name of the annotation to look for; may be {@code null} + * @return {@code true} if the annotation is present (either directly or as a meta-annotation), + * {@code false} otherwise or if either parameter is {@code null} + */ + static boolean isAnnotationPresent(Element element, CharSequence annotationClassName) { + if (element == null || annotationClassName == null) { + return false; + } + return findAnnotation(element, annotationClassName) != null || findMetaAnnotation(element, annotationClassName) != null; + } + + /** + * Retrieves all {@link AnnotationMirror} instances from the given {@link AnnotatedConstruct} + * that match the provided annotation filters. + * + *

If the annotated construct is {@code null}, this method returns an empty list. + * If no annotation filters are provided, all annotations present on the construct are returned. + * Otherwise, only annotations that satisfy all the provided predicates are included in the result.

+ * + *

Example Usage

+ *
{@code
+     * // Retrieve all annotations from a class element
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * List allAnnotations = AnnotationUtils.findAnnotations(typeElement);
+     *
+     * // Retrieve annotations that match a specific condition
+     * List filteredAnnotations = AnnotationUtils.findAnnotations(typeElement,
+     *     annotation -> "com.example.MyAnnotation".contentEquals(annotation.getAnnotationType().asElement().toString()));
+     *
+     * // Retrieve annotations that match multiple conditions
+     * List multiFilteredAnnotations = AnnotationUtils.findAnnotations(typeElement,
+     *     annotation -> isAnnotationPresent(annotation, "com.example.MetaAnnotation"),
+     *     annotation -> annotation.getElementValues().size() > 1);
+     *
+     * // Handle cases where the annotated construct is null
+     * List annotations = AnnotationUtils.findAnnotations(null); // returns empty list
+     * }
+ * + * @param annotatedConstruct the annotated construct to search for annotations, may be {@code null} + * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} + * @return a non-null immutable list of matching {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List findAnnotations(AnnotatedConstruct annotatedConstruct, Predicate... annotationFilters) { + if (annotatedConstruct == null) { + return emptyList(); + } + + List annotations = (List) annotatedConstruct.getAnnotationMirrors(); + if (isEmpty(annotations)) { + return emptyList(); + } + + if (isNotEmpty(annotationFilters)) { + annotations = filterAll(annotations, annotationFilters); + } + + return annotations.isEmpty() ? emptyList() : unmodifiableList(annotations); + } + + /** + * Retrieves all {@link AnnotationMirror} instances from the given {@link TypeMirror}, applying the specified + * annotation filters to narrow down the results. If the type mirror is {@code null}, this method returns an empty list. + * + *

This method delegates to {@link #findAllAnnotations(TypeElement, Predicate[])} after converting the + * {@link TypeMirror} to a {@link TypeElement} using {@link TypeUtils#ofTypeElement(TypeMirror)}. + * + * @param type the type mirror to search for annotations, may be {@code null} + * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} + * @return a non-null immutable list of matching {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List findAllAnnotations(TypeMirror type, Predicate... annotationFilters) { + if (type == null) { + return emptyList(); + } + return findAllAnnotations(ofTypeElement(type), annotationFilters); + } + + @Nonnull + @Immutable + static List findAllAnnotations(TypeElement element, Predicate... annotationFilters) { + if (element == null) { + return emptyList(); + } + List typeElements = getAllTypeElements(element); + + List annotations = typeElements.stream() + .map(AnnotationUtils::getAnnotations) + .flatMap(Collection::stream) + .collect(toList()); + + if (isNotEmpty(annotationFilters)) { + annotations = filterAll(annotations, annotationFilters); + } + + return isEmpty(annotations) ? emptyList() : unmodifiableList(annotations); + } + + /** + * Retrieves all {@link AnnotationMirror} instances from the given {@link Element}, applying the specified + * annotation filters to narrow down the results. If the element is {@code null}, this method returns an empty list. + * + *

This method attempts to resolve the element into a {@link TypeElement}. If successful, it delegates to + * {@link #findAllAnnotations(TypeElement, Predicate[])} to retrieve annotations from the type hierarchy. + * Otherwise, it falls back to using {@link #findAnnotations(AnnotatedConstruct, Predicate[])} directly on the element.

+ * + * @param element the annotated element to search for annotations, may be {@code null} + * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} + * @return a non-null immutable list of matching {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List findAllAnnotations(Element element, Predicate... annotationFilters) { + if (element == null) { + return emptyList(); + } + + TypeElement typeElement = ofTypeElement(element); + + if (typeElement == null) { + return findAnnotations(element, annotationFilters); + } + + return findAllAnnotations(typeElement, annotationFilters); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotated type from the given + * {@link ProcessingEnvironment}. If either the processing environment or the annotated type is {@code null}, + * this method returns an empty list. + * + *

This method uses the fully qualified type name of the provided {@link Type} to locate the corresponding + * annotations. It delegates to the overloaded method that accepts a {@link CharSequence} for the type name.

+ * + * @param processingEnv the processing environment used to retrieve annotations, may be {@code null} + * @param annotatedType the annotated type to search for annotations, may be {@code null} + * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} + * @return a non-null immutable list of {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List findAllAnnotations(ProcessingEnvironment processingEnv, Type annotatedType, Predicate... annotationFilters) { + if (processingEnv == null || annotatedType == null) { + return emptyList(); + } + return findAllAnnotations(processingEnv, annotatedType.getTypeName(), annotationFilters); + } + + /** + * Retrieves all {@link AnnotationMirror} instances of the specified annotated type name from the given + * {@link ProcessingEnvironment}. If either the processing environment or the annotated type name is {@code null}, + * this method returns an empty list. + * + *

This method resolves the annotated type by delegating to {@link TypeUtils#getTypeElement(ProcessingEnvironment, CharSequence)}, + * and then uses the resolved type to find annotations with the provided filters.

+ * + * @param processingEnv the processing environment used to retrieve annotations, may be {@code null} + * @param annotatedTypeName the fully qualified class name of the annotation to look for, may be {@code null} + * @param annotationFilters a varargs array of predicates used to filter annotations; may be empty or {@code null} + * @return a non-null immutable list of matching {@link AnnotationMirror} instances; never {@code null} + */ + @Nonnull + @Immutable + static List findAllAnnotations(ProcessingEnvironment processingEnv, CharSequence annotatedTypeName, Predicate... annotationFilters) { + if (processingEnv == null || annotatedTypeName == null) { + return emptyList(); + } + return findAllAnnotations(getTypeElement(processingEnv, annotatedTypeName), annotationFilters); + } + + /** + * Checks if the given annotation mirror has the same type as the specified {@link Type}. + * + *

This method compares the fully qualified name of the annotation mirror's type with + * the provided type's name. Returns {@code false} if either parameter is {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * AnnotationMirror annotationMirror = ...; // obtain an AnnotationMirror instance
+     * Type annotationType = ...; // obtain a Type instance
+     *
+     * // Check if both are non-null and the types match
+     * boolean isMatch = AnnotationUtils.matchesAnnotationType(annotationMirror, annotationType);
+     *
+     * // Handle cases where the annotation mirror might be null
+     * isMatch = AnnotationUtils.matchesAnnotationType(null, annotationType); // returns false
+     *
+     * // Handle cases where the annotation type might be null
+     * isMatch = AnnotationUtils.matchesAnnotationType(annotationMirror, null); // returns false
+     * }
+ * + * @param annotationMirror the annotation mirror to compare; may be {@code null} + * @param annotationType the target type to match against; may be {@code null} + * @return {@code true} if both parameters are non-null and their types match by name; + * {@code false} otherwise + */ + static boolean matchesAnnotationType(AnnotationMirror annotationMirror, Type annotationType) { + if (annotationMirror == null || annotationType == null) { + return false; + } + return matchesAnnotationTypeName(annotationMirror, annotationType.getTypeName()); + } + + /** + * Checks if the given annotation mirror has the same type as the specified annotation class name. + * + *

This method compares the fully qualified name of the annotation mirror's type with + * the provided annotation class name. Returns {@code false} if either parameter is {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * AnnotationMirror annotationMirror = ...; // obtain an AnnotationMirror instance
+     * String annotationClassName = "com.example.MyAnnotation";
+     *
+     * // Check if both are non-null and the types match
+     * boolean isMatch = AnnotationUtils.matchesAnnotationTypeName(annotationMirror, annotationClassName);
+     *
+     * // Handle cases where the annotation mirror might be null
+     * isMatch = AnnotationUtils.matchesAnnotationTypeName(null, annotationClassName); // returns false
+     *
+     * // Handle cases where the annotation class name might be null
+     * isMatch = AnnotationUtils.matchesAnnotationTypeName(annotationMirror, null); // returns false
+     * }
+ * + * @param annotationMirror the annotation mirror to compare; may be {@code null} + * @param annotationTypeName the target annotation class name to match against; may be {@code null} + * @return {@code true} if both parameters are non-null and their types match by name; + * {@code false} otherwise + */ + static boolean matchesAnnotationTypeName(AnnotationMirror annotationMirror, CharSequence annotationTypeName) { + if (annotationMirror == null || annotationTypeName == null) { + return false; + } + return isSameType(annotationMirror.getAnnotationType(), annotationTypeName); + } + + /** + * Retrieves the name of the attribute method from the given {@link ExecutableElement}. + * + *

This method is typically used to extract the attribute name from an annotation method declaration. + * The returned name corresponds to the method's simple name as defined in the annotation interface.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value();
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attribute names from methods
+     * ExecutableElement valueMethod = ...; // method representing 'value()'
+     * ExecutableElement priorityMethod = ...; // method representing 'priority()'
+     *
+     * String valueName = AnnotationUtils.getAttributeName(valueMethod); // returns "value"
+     * String priorityName = AnnotationUtils.getAttributeName(priorityMethod); // returns "priority"
+     *
+     * // Handle cases where the executable element is null
+     * String name = AnnotationUtils.getAttributeName(null); // returns null
+     * }
+ * + * @param attributeMethod the executable element representing the annotation attribute method, may be {@code null} + * @return the name of the attribute method, or {@code null} if the provided element is {@code null} + */ + static String getAttributeName(ExecutableElement attributeMethod) { + return getMethodName(attributeMethod); + } + + /** + * Checks if the provided executable element represents an annotation attribute method + * with the specified name. + * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value();
+     *     int priority() default 0;
+     * }
+     *
+     * // Check if the executable element corresponds to the "value" method
+     * ExecutableElement valueMethod = ...; // method representing 'value()'
+     * boolean isValueMethod = AnnotationUtils.matchesAttributeMethod(valueMethod, "value"); // returns true
+     *
+     * // Check if the executable element corresponds to the "priority" method
+     * ExecutableElement priorityMethod = ...; // method representing 'priority()'
+     * boolean isPriorityMethod = AnnotationUtils.matchesAttributeMethod(priorityMethod, "priority"); // returns true
+     *
+     * // Handle cases where the executable element is null
+     * boolean result = AnnotationUtils.matchesAttributeMethod(null, "value"); // returns false
+     *
+     * // Handle cases where the attribute name is null or blank
+     * result = AnnotationUtils.matchesAttributeMethod(valueMethod, null); // returns false
+     * result = AnnotationUtils.matchesAttributeMethod(valueMethod, ""); // returns false
+     * }
+ * + * @param attributeMethod the executable element to check, may be {@code null} + * @param attributeName the expected name of the attribute method, may be {@code null} or blank + * @return {@code true} if the method is not null and its name matches the given attribute name; + * {@code false} otherwise + */ + static boolean matchesAttributeMethod(ExecutableElement attributeMethod, String attributeName) { + return attributeMethod != null && Objects.equals(getAttributeName(attributeMethod), attributeName); + } + + /** + * Checks if two given {@link AnnotationValue} instances are equal by comparing their values. + * + *

This method performs a deep comparison of the values contained within the annotation values. + * If both values are {@code null}, they are considered equal. If only one is {@code null}, they are not equal. + * Otherwise, the method compares the actual values using {@link Objects#equals(Object, Object)}.

+ * + *

Example Usage

+ *
{@code
+     * AnnotationValue value1 = ...; // obtain an AnnotationValue instance
+     * AnnotationValue value2 = ...; // obtain another AnnotationValue instance
+     *
+     * // Check if both annotation values are equal
+     * boolean isEqual = AnnotationUtils.matchesAttributeValue(value1, value2);
+     *
+     * // Handle cases where either value is null
+     * isEqual = AnnotationUtils.matchesAttributeValue(null, value2); // returns false
+     * isEqual = AnnotationUtils.matchesAttributeValue(value1, null); // returns false
+     * }
+ * + * @param one the first annotation value to compare; may be {@code null} + * @param another the second annotation value to compare; may be {@code null} + * @return {@code true} if both annotation values are either {@code null} or their contents are equal; + * {@code false} otherwise + */ + static boolean matchesAttributeValue(AnnotationValue one, AnnotationValue another) { + if (one == another) { + return true; + } + if (one == null || another == null) { + return false; + } + Object oneValue = one.getValue(); + Object anotherValue = another.getValue(); + return Objects.equals(oneValue, anotherValue); + } + + /** + * Checks if the value of the given {@link AnnotationValue} matches the specified attribute value. + * + *

This method compares the actual value extracted from the annotation value with the provided + * attribute value. Returns {@code false} if either parameter is {@code null} or if the values do not match.

+ * + *

Example Usage

+ *
{@code
+     * AnnotationValue annotationValue = ...; // obtain an AnnotationValue instance
+     * String expectedValue = "example";
+     * boolean isMatch = AnnotationUtils.matchesAttributeValue(annotationValue, expectedValue); // returns true if values match
+     *
+     * // Handle cases where the annotation value is null
+     * boolean result = AnnotationUtils.matchesAttributeValue(null, expectedValue); // returns false
+     *
+     * // Handle cases where the attribute value is null
+     * result = AnnotationUtils.matchesAttributeValue(annotationValue, null); // returns false
+     * }
+ * + * @param annotationValue the annotation value to compare; may be {@code null} + * @param attributeValue the target value to match against; may be {@code null} + * @return {@code true} if both parameters are non-null and their values match; + * {@code false} otherwise + */ + static boolean matchesAttributeValue(AnnotationValue annotationValue, Object attributeValue) { + return annotationValue != null && Objects.equals(annotationValue.getValue(), attributeValue); + } + + /** + * Checks if the provided annotation value matches the default value of the specified attribute method. + * + *

This method is useful when determining if an attribute value in an annotation is explicitly set or + * if it falls back to the default value declared in the annotation interface.

+ * + *

Example Usage

+ *
{@code
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     * }
+     *
+     * AnnotationMirror annotation = ...; // obtained from an annotated element
+     * ExecutableElement attributeMethod = getAttributeMethod(annotation, "value");
+     * AnnotationValue annotationValue = getAttributeValue(annotation, "value");
+     *
+     * boolean isDefaultValue = matchesDefaultAttributeValue(attributeMethod, annotationValue);
+     * }

+ * + * @param attributeMethod the executable element representing the annotation attribute method, may be {@code null} + * @param annotationValue the annotation value to compare against the default, may be {@code null} + * @return {@code true} if both the attribute method and annotation value are non-null and the value matches the default; + * {@code false} otherwise + */ + static boolean matchesDefaultAttributeValue(ExecutableElement attributeMethod, AnnotationValue annotationValue) { + return attributeMethod != null && matchesAttributeValue(attributeMethod.getDefaultValue(), annotationValue); + } + + /** + * Retrieves a map of attribute names to their corresponding values from the first matching annotation of the specified class + * on the given annotated construct. This includes both explicitly set values and default values for attributes. + * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attributes from an annotated class
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * Map attributes = AnnotationUtils.getAttributesMap(typeElement, MyAnnotation.class);
+     *
+     * // Example output if MyAnnotation is applied with value="custom"
+     * // attributes will contain:
+     * // {
+     * //     "value" = "custom",
+     * //     "priority" = 0
+     * // }
+     *
+     * // Handle cases where the annotated construct is null
+     * attributes = AnnotationUtils.getAttributesMap(null, MyAnnotation.class); // returns empty map
+     *
+     * // Handle cases where the annotation class is null
+     * attributes = AnnotationUtils.getAttributesMap(typeElement, null); // returns empty map
+     * }
+ * + * @param annotatedConstruct the annotated construct (e.g., a class, method, or field) that may contain the annotation, + * may be {@code null} + * @param annotationClass the annotation class used to locate the annotation on the construct, must not be {@code null} + * @return a non-null immutable map of attribute names to their corresponding values; never {@code null}, + * returns an empty map if no annotation is found, the construct is {@code null}, or the annotation class is {@code null} + */ + @Nonnull + @Immutable + static Map getAttributesMap(AnnotatedConstruct annotatedConstruct, Class annotationClass) { + return getAttributesMap(annotatedConstruct, annotationClass, WITH_DEFAULT); + } + + /** + * Retrieves a map of attribute names to their corresponding values from the first matching annotation of the specified class + * on the given annotated construct. This includes both explicitly set values and default values for attributes if the + * {@code withDefault} flag is set to {@code true}. + * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attributes from an annotated class
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * Map attributes = AnnotationUtils.getAttributesMap(typeElement, MyAnnotation.class, true);
+     *
+     * // Example output if MyAnnotation is applied with value="custom"
+     * // attributes will contain:
+     * // {
+     * //     "value" = "custom",
+     * //     "priority" = 0
+     * // }
+     *
+     * // Handle cases where the annotated construct is null
+     * attributes = AnnotationUtils.getAttributesMap(null, MyAnnotation.class, true); // returns empty map
+     *
+     * // Handle cases where the annotation class is null
+     * attributes = AnnotationUtils.getAttributesMap(typeElement, null, true); // returns empty map
+     * }
+ * + * @param annotatedConstruct the annotated construct (e.g., a class, method, or field) that may contain the annotation, + * may be {@code null} + * @param annotationClass the annotation class used to locate the annotation on the construct, must not be {@code null} + * @param withDefault flag indicating whether to include default values for attributes that are not explicitly set; + * if {@code true}, default values will be included where applicable + * @return a non-null immutable map of attribute names to their corresponding values; never {@code null}, + * returns an empty map if no annotation is found, the construct is {@code null}, or the annotation class is {@code null} + */ + @Nonnull + @Immutable + static Map getAttributesMap(AnnotatedConstruct annotatedConstruct, Class annotationClass, boolean withDefault) { + return getAttributesMap(getAnnotation(annotatedConstruct, annotationClass), withDefault); + } + + /** + * Retrieves a map of attribute names to their corresponding values from the specified annotation. + * This includes both explicitly set values and default values for attributes. + * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attributes from an annotation instance
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * Map attributes = AnnotationUtils.getAttributesMap(annotation);
+     *
+     * // Example output if MyAnnotation is applied with value="custom"
+     * // attributes will contain:
+     * // {
+     * //     "value" = "custom",
+     * //     "priority" = 0
+     * // }
+     *
+     * // Handle cases where the annotation is null
+     * Map emptyMap = AnnotationUtils.getAttributesMap(null); // returns empty map
+     * }
+ * + * @param annotation the specified annotation, may be {@code null} + * @return a non-null immutable map of attribute names to their corresponding values; never {@code null}, + * returns an empty map if the annotation is {@code null} + */ + @Nonnull + @Immutable + static Map getAttributesMap(AnnotationMirror annotation) { + return getAttributesMap(annotation, WITH_DEFAULT); + } + + /** + * Retrieves a map of attribute names to their corresponding values from the specified annotation. + * This includes both explicitly set values and default values for attributes if the + * {@code withDefault} flag is set to {@code true}. + * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attributes from an annotation instance with default values
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * Map attributes = AnnotationUtils.getAttributesMap(annotation, true);
+     *
+     * // Example output if MyAnnotation is applied with value="custom"
+     * // attributes will contain:
+     * // {
+     * //     "value" = "custom",
+     * //     "priority" = 0
+     * // }
+     *
+     * // Retrieve attributes without including default values
+     * Map explicitAttributes = AnnotationUtils.getAttributesMap(annotation, false);
+     *
+     * // Handle cases where the annotation is null
+     * Map emptyMap = AnnotationUtils.getAttributesMap(null, true); // returns empty map
+     * }
+ * + * @param annotation the specified annotation, may be {@code null} + * @param withDefault flag indicating whether to include default values for attributes that are not explicitly set; + * if {@code true}, default values will be included where applicable + * @return a non-null immutable map of attribute names to their corresponding values; never {@code null}, + * returns an empty map if the annotation is {@code null} + */ + @Nonnull + @Immutable + static Map getAttributesMap(AnnotationMirror annotation, boolean withDefault) { + Map attributes = getElementValues(annotation, withDefault); + int size = attributes.size(); + if (size < 1) { + return emptyMap(); + } + Map attributesMap = newFixedLinkedHashMap(size); + for (Entry entry : attributes.entrySet()) { + ExecutableElement attributeMethod = entry.getKey(); + String attributeName = getAttributeName(attributeMethod); + Object attributeValue = getAttribute(entry); + attributesMap.put(attributeName, attributeValue); + } + return unmodifiableMap(attributesMap); + } + + /** + * Retrieves the map of annotation attribute methods to their corresponding values from the specified + * {@link AnnotatedConstruct} and annotation class. This method finds the first matching annotation on the construct + * and returns all its declared attribute values, including default values. + * + *

This method is a convenience overload that defaults to including default values. If the annotated construct + * is {@code null} or the annotation class is not present, an empty map is returned. + * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attribute values from an annotated class
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * Map attributes = AnnotationUtils.getElementValues(typeElement, MyAnnotation.class);
+     *
+     * // Example output if MyAnnotation is applied with value="custom"
+     * // attributes will contain:
+     * // {
+     * //     value(): "custom",
+     * //     priority(): 0
+     * // }
+     *
+     * // Handle cases where the annotated construct is null
+     * Map emptyMap = AnnotationUtils.getElementValues(null, MyAnnotation.class); // returns empty map
+     *
+     * // Handle cases where the annotation class is null
+     * emptyMap = AnnotationUtils.getElementValues(typeElement, null); // returns empty map
+     * }
+ * + * @param annotatedConstruct the annotated construct (e.g., a class, method, or field) that may contain the annotation, + * may be {@code null} + * @param annotationClass the annotation class used to locate the annotation on the construct, must not be {@code null} + * @return a non-null immutable map of executable elements (attribute methods) to their annotation values; never {@code null}, + * returns an empty map if no annotation is found or if the construct is {@code null} + */ + @Nonnull + @Immutable + static Map getElementValues(AnnotatedConstruct annotatedConstruct, Class annotationClass) { + return getElementValues(annotatedConstruct, annotationClass, WITH_DEFAULT); + } + + /** + * Retrieves the map of annotation attribute methods to their corresponding values from the specified + * {@link AnnotatedConstruct} and annotation class. This method finds the first matching annotation on the construct + * and returns all its declared attribute values, including default values if enabled. + * + *

If the annotated construct is {@code null} or the annotation class is not present, an empty map is returned. + * If the {@code withDefault} flag is set to {@code true}, this method includes default values for attributes + * that are not explicitly set.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attribute values from an annotated class with default values
+     * TypeElement typeElement = ...; // obtain a TypeElement
+     * Map attributes = AnnotationUtils.getElementValues(typeElement, MyAnnotation.class, true);
+     *
+     * // Example output if MyAnnotation is applied with value="custom"
+     * // attributes will contain:
+     * // {
+     * //     value(): "custom",
+     * //     priority(): 0
+     * // }
+     *
+     * // Retrieve attribute values without including default values
+     * Map explicitAttributes = AnnotationUtils.getElementValues(typeElement, MyAnnotation.class, false);
+     *
+     * // Handle cases where the annotated construct is null
+     * Map emptyMap = AnnotationUtils.getElementValues(null, MyAnnotation.class, true); // returns empty map
+     *
+     * // Handle cases where the annotation class is null
+     * emptyMap = AnnotationUtils.getElementValues(typeElement, null, true); // returns empty map
+     * }
+ * + * @param annotatedConstruct the annotated construct (e.g., a class, method, or field) that may contain the annotation, + * may be {@code null} + * @param annotationClass the annotation class used to locate the annotation on the construct, must not be {@code null} + * @param withDefault flag indicating whether to include default values for attributes that are not explicitly set; + * if {@code true}, default values will be included where applicable + * @return a non-null immutable map of executable elements (attribute methods) to their annotation values; never {@code null}, + * returns an empty map if no annotation is found or if the construct is {@code null} + */ + @Nonnull + @Immutable + static Map getElementValues(AnnotatedConstruct annotatedConstruct, Class annotationClass, boolean withDefault) { + return getElementValues(getAnnotation(annotatedConstruct, annotationClass), withDefault); + } + + /** + * Retrieves a map of annotation attribute methods to their corresponding values from the specified + * {@link AnnotationMirror}. This method includes both explicitly set values and default values for attributes. + * + *

If the provided annotation is {@code null}, an empty map is returned.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attribute values from an annotation instance
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * Map attributes = AnnotationUtils.getElementValues(annotation);
+     *
+     * // Example output if MyAnnotation is applied with value="custom"
+     * // attributes will contain:
+     * // {
+     * //     value(): "custom",
+     * //     priority(): 0
+     * // }
+     *
+     * // Handle cases where the annotation is null
+     * Map emptyMap = AnnotationUtils.getElementValues(null); // returns empty map
+     * }
+ * + * @param annotation the annotation mirror to extract attribute values from, may be {@code null} + * @return a non-null immutable map of executable elements (attribute methods) to their annotation values; + * never {@code null}, returns an empty map if the annotation is {@code null} + */ + @Nonnull + @Immutable + static Map getElementValues(AnnotationMirror annotation) { + return getElementValues(annotation, WITH_DEFAULT); + } + + /** + * Retrieves a map of annotation attribute methods to their corresponding values from the specified + * {@link AnnotationMirror}. This method includes both explicitly set values and default values for attributes + * if the {@code withDefault} flag is set to {@code true}. + * + *

If the provided annotation is {@code null}, an empty map is returned.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve attribute values from an annotation instance with default values
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * Map attributes = AnnotationUtils.getElementValues(annotation, true);
+     *
+     * // Example output if MyAnnotation is applied with value="custom"
+     * // attributes will contain:
+     * // {
+     * //     value(): "custom",
+     * //     priority(): 0
+     * // }
+     *
+     * // Retrieve attribute values without including default values
+     * Map explicitAttributes = AnnotationUtils.getElementValues(annotation, false);
+     *
+     * // Handle cases where the annotation is null
+     * Map emptyMap = AnnotationUtils.getElementValues(null, true); // returns empty map
+     * }
+ * + * @param annotation the annotation mirror to extract attribute values from, may be {@code null} + * @param withDefault flag indicating whether to include default values for attributes that are not explicitly set; + * if {@code true}, default values will be included where applicable + * @return a non-null immutable map of executable elements (attribute methods) to their annotation values; + * never {@code null}, returns an empty map if the annotation is {@code null} + */ + @Nonnull + @Immutable + static Map getElementValues(AnnotationMirror annotation, boolean withDefault) { + if (annotation == null) { + return emptyMap(); + } + DeclaredType annotationType = annotation.getAnnotationType(); + List attributeMethods = getDeclaredMethods(annotationType); + int size = attributeMethods.size(); + Map attributes = newFixedLinkedHashMap(size); + Map elementValues = annotation.getElementValues(); + for (int i = 0; i < size; i++) { + ExecutableElement attributeMethod = attributeMethods.get(i); + AnnotationValue annotationValue = elementValues.get(attributeMethod); + if (withDefault && annotationValue == null) { + annotationValue = attributeMethod.getDefaultValue(); + } + if (annotationValue != null) { + attributes.put(attributeMethod, annotationValue); + } + } + return unmodifiableMap(attributes); + } + + /** + * Retrieves the attribute method and its corresponding annotation value from the specified annotation + * based on the given attribute name. If no explicit value is found and {@code withDefault} is true, + * it attempts to find and return the default value for the attribute. + * + *

If the provided annotation is null or the attribute name is blank, this method returns null.

+ * + *

Example Usage

+ *
{@code
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * String attributeName = "value"; // the name of the attribute to retrieve
+     *
+     * // Retrieve an attribute with default value lookup enabled
+     * Map.Entry attributeEntry = AnnotationUtils.getElementValue(annotation, attributeName, true);
+     *
+     * if (attributeEntry != null) {
+     *     ExecutableElement attributeMethod = attributeEntry.getKey();
+     *     AnnotationValue annotationValue = attributeEntry.getValue();
+     *     // process attribute method and value
+     * }
+     *
+     * // Retrieve an attribute without default value lookup
+     * attributeEntry = AnnotationUtils.getElementValue(annotation, attributeName, false);
+     *
+     * // Handle cases where the annotation is null
+     * attributeEntry = AnnotationUtils.getElementValue(null, attributeName, true); // returns null
+     *
+     * // Handle cases where the attribute name is blank or null
+     * attributeEntry = AnnotationUtils.getElementValue(annotation, null, true); // returns null
+     * }
+ * + * @param annotation the annotation mirror to extract the attribute value from, may be {@code null} + * @param attributeName the name of the attribute method to look for, may be blank + * @param withDefault flag indicating whether to include the default value if the attribute is not explicitly set; + * if true, the method will attempt to find and return the default value + * @return an entry containing the executable element (attribute method) and its corresponding annotation value; + * returns null if the annotation is null, the attribute name is blank, or the attribute method cannot be found + */ + @Nullable + @Immutable + static Entry getElementValue(AnnotationMirror annotation, String attributeName, boolean withDefault) { + if (annotation == null || isBlank(attributeName)) { + return null; + } + + ExecutableElement attributeMethod = null; + AnnotationValue annotationValue = null; + Map elementValues = annotation.getElementValues(); + for (Entry entry : elementValues.entrySet()) { + attributeMethod = entry.getKey(); + if (matchesAttributeMethod(attributeMethod, attributeName)) { + annotationValue = entry.getValue(); + break; + } + } + + if (withDefault && annotationValue == null) { // not found if the default value is required + DeclaredType annotationType = annotation.getAnnotationType(); + List attributeMethods = findDeclaredMethods(annotationType, method -> !elementValues.containsKey(method)); + int size = attributeMethods.size(); + for (int i = 0; i < size; i++) { + attributeMethod = attributeMethods.get(i); + if (matchesAttributeMethod(attributeMethod, attributeName)) { + annotationValue = attributeMethod.getDefaultValue(); + break; + } + } + } + + return immutableEntry(attributeMethod, annotationValue); + } + + /** + * Retrieves the attribute method and its corresponding annotation value from the specified element values map + * based on the given attribute name. + * + *

This method searches through the provided map of executable elements (attribute methods) to their annotation + * values to find a matching attribute by name. If no match is found, it returns {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Assume elementValues contains:
+     * // {
+     * //     value(): "custom",
+     * //     priority(): 5
+     * // }
+     *
+     * // Retrieve an existing attribute
+     * Entry entry = AnnotationUtils.getElementValue(elementValues, "value");
+     * if (entry != null) {
+     *     String value = (String) entry.getValue().getValue(); // returns "custom"
+     * }
+     *
+     * // Retrieve a non-existent attribute
+     * entry = AnnotationUtils.getElementValue(elementValues, "nonExistent"); // returns null
+     *
+     * // Handle cases where the element values map is null or empty
+     * entry = AnnotationUtils.getElementValue(null, "value"); // returns null
+     * }
+ * + * @param elementValues the map of executable elements (attribute methods) to their annotation values; + * may be {@code null} or empty + * @param attributeName the name of the attribute method to look for; may be {@code null} or blank + * @return an entry containing the executable element (attribute method) and its corresponding annotation value; + * returns {@code null} if the element values map is empty, the attribute name is blank, or no matching + * attribute is found + */ + @Nullable + static Entry getElementValue(Map elementValues, String attributeName) { + if (isEmpty(elementValues)) { + return null; + } + for (Entry entry : elementValues.entrySet()) { + if (matchesAttributeMethod(entry.getKey(), attributeName)) { + return entry; + } + } + return null; + } + + /** + * Retrieves the value of the specified attribute from the given annotation. + * + *

This method attempts to find the attribute by name in the provided annotation. If the attribute is not explicitly set, + * it will attempt to retrieve the default value associated with that attribute. If the attribute cannot be resolved or no value is found, + * this method returns {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve the value of the "value" attribute
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * String value = AnnotationUtils.getAttribute(annotation, "value"); // returns "default" or explicit value
+     *
+     * // Retrieve the value of the "priority" attribute
+     * int priority = AnnotationUtils.getAttribute(annotation, "priority"); // returns 0 or explicit value
+     *
+     * // Handle cases where the annotation is null
+     * String result = AnnotationUtils.getAttribute(null, "value"); // returns null
+     *
+     * // Handle cases where the attribute name is blank or null
+     * result = AnnotationUtils.getAttribute(annotation, null); // returns null
+     * }
+ * + * @param the type of the attribute value to return + * @param annotation the annotation mirror to extract the attribute value from; may be {@code null} + * @param attributeName the name of the attribute method to look for; may be blank or {@code null} + * @return the value of the specified attribute if found, or the default value if available; + * returns {@code null} if the annotation is {@code null}, the attribute name is blank, + * or the attribute cannot be resolved + */ + @Nullable + static T getAttribute(AnnotationMirror annotation, String attributeName) { + return getAttribute(annotation, attributeName, WITH_DEFAULT); + } + + /** + * Retrieves the value of the specified attribute from the given annotation, optionally including the default value. + * + *

This method attempts to find the attribute by name in the provided annotation. If the attribute is not explicitly set, + * and the {@code withDefault} flag is set to {@code true}, it will attempt to retrieve the default value associated + * with that attribute. If the attribute cannot be resolved or no value is found, this method returns {@code null}.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Retrieve the value of the "value" attribute including default
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * String value = AnnotationUtils.getAttribute(annotation, "value", true); // returns "default" or explicit value
+     *
+     * // Retrieve the value of the "priority" attribute without default lookup
+     * int priority = AnnotationUtils.getAttribute(annotation, "priority", false); // returns 0 only if explicitly set
+     *
+     * // Handle cases where the annotation is null
+     * String result = AnnotationUtils.getAttribute(null, "value", true); // returns null
+     *
+     * // Handle cases where the attribute name is blank or null
+     * result = AnnotationUtils.getAttribute(annotation, null, true); // returns null
+     * }
+ * + * @param the type of the attribute value to return + * @param annotation the annotation mirror to extract the attribute value from; may be {@code null} + * @param attributeName the name of the attribute method to look for; may be blank or {@code null} + * @param withDefault flag indicating whether to include the default value if the attribute is not explicitly set; + * if {@code true}, the method will attempt to find and return the default value + * @return the value of the specified attribute if found, or the default value if available; + * returns {@code null} if the annotation is {@code null}, the attribute name is blank, + * or the attribute cannot be resolved + */ + @Nullable + static T getAttribute(AnnotationMirror annotation, String attributeName, boolean withDefault) { + Entry attributeEntry = getElementValue(annotation, attributeName, withDefault); + return getAttribute(attributeEntry); + } + + /** + * Retrieves the value of the specified attribute from the provided entry containing the attribute method + * and its corresponding annotation value. + * + *

If the entry is null or either the attribute method or annotation value is unresolved, this method returns {@code null}. + * Otherwise, it delegates to the default {@link AnnotationValueVisitor} to extract the attribute value.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     *     int priority() default 0;
+     * }
+     *
+     * // Assume elementValue contains the entry for "value" attribute
+     * Entry elementValue = ...; // obtain from AnnotationUtils.getElementValue()
+     * String value = AnnotationUtils.getAttribute(elementValue); // returns "default" or explicit value
+     *
+     * // Handle cases where the element value entry is null
+     * String result = AnnotationUtils.getAttribute(null); // returns null
+     * }
+ * + * @param the expected type of the attribute value + * @param elementValue an entry containing the attribute method and its corresponding annotation value; + * may be {@code null} + * @return the resolved value of the attribute if found; returns {@code null} if the entry is null, + * or if either the attribute method or annotation value is unresolved + */ + @Nullable + static T getAttribute(Entry elementValue) { + if (elementValue == null) { + return null; + } + + ExecutableElement attributeMethod = elementValue.getKey(); + AnnotationValue annotationValue = elementValue.getValue(); + + return (T) annotationValue.accept(DEFAULT_ANNOTATION_VALUE_VISITOR, attributeMethod); + } + + /** + * Retrieves the value of the default attribute method named {@code "value"} from the specified annotation. + * + *

This method delegates to {@link #getAttribute(AnnotationMirror, String)} to obtain the value of the annotation's + * {@code value()} method. Returns {@code null} if the annotation is {@code null} or if the value cannot be resolved.

+ * + *

Example Usage

+ *
{@code
+     * // Given an annotation interface:
+     * public @interface MyAnnotation {
+     *     String value() default "default";
+     * }
+     *
+     * // Retrieve the "value" attribute from an annotation instance
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * String value = AnnotationUtils.getValue(annotation); // returns "default" or explicit value
+     *
+     * // Handle cases where the annotation is null
+     * String result = AnnotationUtils.getValue(null); // returns null
+     * }
+ * + * @param the expected type of the attribute value + * @param annotation the annotation mirror to extract the value from; may be {@code null} + * @return the resolved value of the annotation's {@code value()} method if found; + * returns {@code null} if the annotation is {@code null} or the value cannot be resolved + */ + @Nullable + static T getValue(AnnotationMirror annotation) { + return getAttribute(annotation, VALUE_ATTRIBUTE_NAME); + } + + /** + * Retrieves the {@link ElementType} array from the specified annotation. + * + * @param annotation the specified annotation, may be {@code null} + * @return a non-null array of {@link ElementType}; never {@code null}, returns an empty array if the annotation is {@code null} + */ + /** + * Retrieves the {@link ElementType} array from the specified annotation. + * + *

This method checks the annotation's type for the presence of a {@link Target} annotation, + * which defines the element types the annotation can be applied to. If the provided annotation + * is {@code null}, this method returns an empty array.

+ * + *

Example Usage

+ *
{@code
+     * AnnotationMirror annotation = ...; // obtain an AnnotationMirror instance
+     * ElementType[] elementTypes = AnnotationUtils.getElementTypes(annotation);
+     *
+     * // Handle cases where the annotation is null
+     * ElementType[] emptyTypes = AnnotationUtils.getElementTypes(null); // returns empty array
+     * }
+ * + * @param annotation the specified annotation, may be {@code null} + * @return a non-null array of {@link ElementType}; never {@code null}, returns an empty array if the annotation is {@code null} + */ + @Nonnull + static ElementType[] getElementTypes(AnnotationMirror annotation) { + return annotation == null ? EMPTY_ELEMENT_TYPE_ARRAY : getElementTypes(annotation.getAnnotationType()); + } + + /** + * Retrieves the {@link ElementType} array from the specified annotation type by checking + * the {@link Target} annotation associated with it. If the annotation type does not have a + * {@link Target} annotation, an empty array is returned. + * + *

Example Usage

+ *
{@code
+     * // Given an annotation type:
+     * public @interface MyAnnotation {
+     * }
+     *
+     * DeclaredType annotationType = ...; // obtain a DeclaredType for MyAnnotation
+     * ElementType[] elementTypes = AnnotationUtils.getElementTypes(annotationType);
+     *
+     * // If MyAnnotation is annotated with @Target(ElementType.TYPE)
+     * // elementTypes will contain: { ElementType.TYPE }
+     *
+     * // Handle cases where the annotation type does not have a @Target annotation
+     * ElementType[] emptyTypes = AnnotationUtils.getElementTypes(annotationType); // returns empty array
+     * }
+ * + * @param annotationType the declared type of the annotation + * @return a non-null immutable array of {@link ElementType}; never {@code null}, + * returns an empty array if no {@link Target} annotation is present + */ + @Nonnull + static ElementType[] getElementTypes(DeclaredType annotationType) { + AnnotationMirror targetAnnotation = findAnnotation(annotationType, Target.class); + ElementType[] elementTypes = getValue(targetAnnotation); + return elementTypes == null ? EMPTY_ELEMENT_TYPE_ARRAY : elementTypes; + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ClassUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ClassUtils.java new file mode 100644 index 000000000..1c2239399 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ClassUtils.java @@ -0,0 +1,109 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import io.microsphere.util.Utils; + +import javax.lang.model.type.TypeMirror; + +import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; +import static io.microsphere.constants.SymbolConstants.DOLLAR_CHAR; +import static io.microsphere.constants.SymbolConstants.DOT_CHAR; +import static io.microsphere.util.ClassLoaderUtils.getClassLoader; +import static io.microsphere.util.ClassLoaderUtils.resolveClass; + +/** + * The utilities class for {@link Class} + * + * @author Mercy + * @see Class + * @since 1.0.0 + */ +public interface ClassUtils extends Utils { + + /** + * Gets the fully qualified class name from the given {@link TypeMirror}. + * + *

+ * This method is useful when working with annotation processors or other + * compile-time code analysis tools that deal with type information represented + * as a {@link TypeMirror}. + *

+ * + *

Example Usage

+ *
+     * TypeMirror type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass").asType();
+     * String className = getClassName(type); // returns "com.example.MyClass"
+     * 
+ * + * @param type the type mirror to extract the class name from + * @return the fully qualified class name as a String + */ + static String getClassName(TypeMirror type) { + return ofTypeElement(type).getQualifiedName().toString(); + } + + /** + * Loads the class represented by the given {@link TypeMirror}. + * + *

This method derives the fully qualified class name from the type mirror and + * delegates to the other {@link #loadClass(String)} method for class loading. + * It is particularly useful in annotation processors or compile-time tools where + * types are primarily accessed via their mirror representations. + * + *

Example Usage

+ *
+     * TypeMirror type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass").asType();
+     * Class clazz = ClassUtils.loadClass(type); // loads com.example.MyClass
+     * 
+ * + * @param type the type mirror representing the class to load + * @return the resolved {@link Class}, or {@code null} if the class cannot be found + */ + static Class loadClass(TypeMirror type) { + return loadClass(getClassName(type)); + } + + /** + * Loads the class represented by the given fully qualified class name. + * + *

This method attempts to resolve the class using the provided class name and the class loader + * obtained from {@link ClassUtils}. If the class is not found, an attempt is made to resolve it + * as a nested or inner class by replacing the last dot ({@code .}) with a dollar sign ({@code $}). + * + *

Example Usage

+ *
+     * Class clazz = ClassUtils.loadClass("com.example.MyClass");
+     * // If MyClass is an inner class, this may also attempt to load "com.example.My$Class"
+     * 
+ * + * @param className the fully qualified name of the class to load + * @return the resolved {@link Class}, or {@code null} if the class cannot be found + */ + static Class loadClass(String className) { + ClassLoader classLoader = getClassLoader(ClassUtils.class); + Class klass = resolveClass(className, classLoader); + if (klass == null) { + int index = className.lastIndexOf(DOT_CHAR); + if (index > 0) { + className = className.substring(0, index) + DOLLAR_CHAR + className.substring(index + 1); + } + } + return resolveClass(className, classLoader); + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ConstructorUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ConstructorUtils.java new file mode 100644 index 000000000..eb878ed96 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ConstructorUtils.java @@ -0,0 +1,293 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import io.microsphere.annotation.Immutable; +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.util.Utils; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import java.lang.reflect.Constructor; +import java.lang.reflect.Type; +import java.util.List; +import java.util.function.Predicate; + +import static io.microsphere.lang.model.util.ElementUtils.filterElements; +import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypes; +import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.collection.ListUtils.first; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static java.util.Collections.emptyList; +import static javax.lang.model.util.ElementFilter.constructorsIn; + +/** + * The utils class for {@link Constructor constructor} + * + * @author Mercy + * @see Constructor + * @see ExecutableElement + * @see ElementKind#CONSTRUCTOR + * @since 1.0.0 + */ +public interface ConstructorUtils extends Utils { + + /** + * Retrieves the list of declared constructors from the provided {@link TypeElement}. + *

+ * This method provides a null-safe way to obtain the constructors of a given type. + * If the input {@code type} is {@code null}, an empty list is returned. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * List constructors = getDeclaredConstructors(typeElement);
+     * if (!constructors.isEmpty()) {
+     *     for (ExecutableElement constructor : constructors) {
+     *         System.out.println("Constructor: " + constructor);
+     *     }
+     * } else {
+     *     System.out.println("No constructors found.");
+     * }
+     * }
+ * + *

+ * This method is particularly useful when processing annotations during compilation, + * where it is necessary to inspect the constructors of a class without throwing exceptions + * for null inputs. + *

+ * + * @param type the {@link TypeElement} representing the type to retrieve constructors from + * @return a {@link List} of {@link ExecutableElement} objects representing the declared constructors; + * never {@code null}, but may be empty if no constructors are found or if the input is {@code null} + */ + @Nonnull + @Immutable + static List getDeclaredConstructors(TypeElement type) { + return type == null ? emptyList() : getDeclaredConstructors(type.asType()); + } + + /** + * Retrieves the list of declared constructors from the provided {@link TypeMirror}. + *

+ * This method provides a null-safe way to obtain the constructors of a given type. + * If the input {@code type} is {@code null}, an empty list is returned. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * List constructors = getDeclaredConstructors(typeMirror);
+     * if (!constructors.isEmpty()) {
+     *     for (ExecutableElement constructor : constructors) {
+     *         System.out.println("Constructor: " + constructor);
+     *     }
+     * } else {
+     *     System.out.println("No constructors found.");
+     * }
+     * }
+ * + *

+ * This method is particularly useful when processing annotations during compilation, + * where it is necessary to inspect the constructors of a class without throwing exceptions + * for null inputs. + *

+ * + * @param type the {@link TypeMirror} representing the type to retrieve constructors from + * @return a {@link List} of {@link ExecutableElement} objects representing the declared constructors; + * never {@code null}, but may be empty if no constructors are found or if the input is {@code null} + */ + @Nonnull + @Immutable + static List getDeclaredConstructors(TypeMirror type) { + return type == null ? emptyList() : findDeclaredConstructors(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Finds a declared constructor in the specified {@link TypeElement} that matches the given parameter types. + *

+ * This method provides a null-safe way to locate a constructor based on its parameter types. + * If the input {@code type} is {@code null}, or no matching constructor is found, {@code null} is returned. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * Type[] paramTypes = new Type[] { String.class, int.class };
+     * ExecutableElement constructor = findConstructor(typeElement, paramTypes);
+     * if (constructor != null) {
+     *     System.out.println("Found constructor: " + constructor);
+     * } else {
+     *     System.out.println("Constructor not found.");
+     * }
+     * }
+ * + * @param type the {@link TypeElement} representing the type to search for constructors + * @param parameterTypes the array of {@link Type} objects representing the parameter types to match + * @return the matched {@link ExecutableElement} representing the constructor; may be {@code null} + */ + @Nullable + static ExecutableElement findConstructor(TypeElement type, Type... parameterTypes) { + return type == null ? null : findConstructor(type.asType(), parameterTypes); + } + + /** + * Finds a declared constructor in the specified {@link TypeMirror} that matches the given parameter types. + *

+ * This method provides a null-safe way to locate a constructor based on its parameter types. + * If the input {@code type} is {@code null}, or no matching constructor is found, {@code null} is returned. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * Type[] paramTypes = new Type[] { String.class, int.class };
+     * ExecutableElement constructor = findConstructor(typeMirror, paramTypes);
+     * if (constructor != null) {
+     *     System.out.println("Found constructor: " + constructor);
+     * } else {
+     *     System.out.println("Constructor not found.");
+     * }
+     * }
+ * + * @param type the {@link TypeMirror} representing the type to search for constructors + * @param parameterTypes the array of {@link Type} objects representing the parameter types to match + * @return the matched {@link ExecutableElement} representing the constructor; may be {@code null} + */ + @Nullable + static ExecutableElement findConstructor(TypeMirror type, Type... parameterTypes) { + if (type == null) { + return null; + } + return first(findDeclaredConstructors(type, constructor -> matchParameterTypes(constructor, parameterTypes))); + } + + /** + * Retrieves and filters the list of declared constructors from the provided {@link TypeElement}. + *

+ * This method provides a null-safe way to obtain the constructors of a given type. + * If the input {@code type} is {@code null}, an empty list is returned. + * The provided filters can be used to selectively include only those constructors that match the criteria. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * // Get all declared constructors
+     * List allConstructors = findDeclaredConstructors(typeElement);
+     *
+     * // Get only constructors with specific parameter types
+     * List filteredConstructors = findDeclaredConstructors(typeElement,
+     *     constructor -> matchParameterTypes(constructor, String.class, int.class));
+     *
+     * if (!filteredConstructors.isEmpty()) {
+     *     for (ExecutableElement constructor : filteredConstructors) {
+     *         System.out.println("Matching Constructor: " + constructor);
+     *     }
+     * } else {
+     *     System.out.println("No matching constructors found.");
+     * }
+     * }
+ * + * @param type the {@link TypeElement} representing the type to retrieve constructors from + * @param constructorFilters optional predicates to filter the constructors; if none are provided, all constructors are included + * @return a {@link List} of {@link ExecutableElement} objects representing the filtered declared constructors; + * never {@code null}, but may be empty if no matching constructors are found or if the input is {@code null} + */ + @Nonnull + @Immutable + static List findDeclaredConstructors(TypeElement type, Predicate... constructorFilters) { + return type == null ? emptyList() : findDeclaredConstructors(type.asType(), constructorFilters); + } + + /** + * Retrieves and filters the list of declared constructors from the provided {@link TypeMirror}. + *

+ * This method provides a null-safe way to obtain the constructors of a given type. + * If the input {@code type} is {@code null}, an empty list is returned. + * The provided filters can be used to selectively include only those constructors that match the criteria. + *

+ * + * @param type the {@link TypeMirror} representing the type to retrieve constructors from + * @param constructorFilters optional predicates to filter the constructors; if none are provided, all constructors are included + * @return a {@link List} of {@link ExecutableElement} objects representing the filtered declared constructors; + * never {@code null}, but may be empty if no matching constructors are found or if the input is {@code null} + */ + @Nonnull + @Immutable + static List findDeclaredConstructors(TypeMirror type, Predicate... constructorFilters) { + return filterDeclaredConstructors(type, constructorFilters); + } + + /** + * Filters and retrieves the list of declared constructors from the provided {@link TypeMirror}. + *

+ * This method is responsible for extracting all declared constructors from the given type + * and applying the specified filters to narrow down the results. If the input {@code type} + * is {@code null}, an empty list is returned. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     *
+     * // Get all declared constructors
+     * List allConstructors = filterDeclaredConstructors(typeMirror);
+     *
+     * // Get only constructors with specific parameter types
+     * List filteredConstructors = filterDeclaredConstructors(typeMirror,
+     *     constructor -> matchParameterTypes(constructor, String.class, int.class));
+     *
+     * if (!filteredConstructors.isEmpty()) {
+     *     for (ExecutableElement constructor : filteredConstructors) {
+     *         System.out.println("Matching Constructor: " + constructor);
+     *     }
+     * } else {
+     *     System.out.println("No matching constructors found.");
+     * }
+     * }
+ * + * @param type the {@link TypeMirror} representing the type to retrieve constructors from + * @param constructorFilters optional predicates to filter the constructors; if none are provided, all constructors are included + * @return a {@link List} of {@link ExecutableElement} objects representing the filtered declared constructors; + * never {@code null}, but may be empty if no matching constructors are found or if the input is {@code null} + */ + @Nonnull + @Immutable + static List filterDeclaredConstructors(TypeMirror type, Predicate... constructorFilters) { + if (type == null) { + return emptyList(); + } + + List declaredMembers = getDeclaredMembers(type, false); + if (isEmpty(declaredMembers)) { + return emptyList(); + } + + List constructors = constructorsIn(declaredMembers); + + return filterElements(constructors, constructorFilters); + } + +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ElementUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ElementUtils.java new file mode 100644 index 000000000..f3d708d8a --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ElementUtils.java @@ -0,0 +1,734 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import io.microsphere.annotation.Immutable; +import io.microsphere.annotation.Nonnull; +import io.microsphere.util.Utils; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.VariableElement; +import java.lang.annotation.ElementType; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +import static io.microsphere.lang.model.util.TypeUtils.isSameType; +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.lang.function.Predicates.and; +import static io.microsphere.reflect.TypeUtils.getTypeNames; +import static io.microsphere.util.ArrayUtils.isNotEmpty; +import static io.microsphere.util.ArrayUtils.length; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static java.util.stream.Collectors.toList; +import static javax.lang.model.element.ElementKind.CLASS; +import static javax.lang.model.element.ElementKind.OTHER; +import static javax.lang.model.element.ElementKind.valueOf; +import static javax.lang.model.element.Modifier.PUBLIC; +import static javax.lang.model.element.Modifier.STATIC; + +/** + * The utility class for {@link Element} + * + * @author Mercy + * @see Element + * @see Enum + * @since 1.0.0 + */ +public interface ElementUtils extends Utils { + + /** + * Returns {@code true} if the specified {@link ElementKind} represents a class-like element, + * including: + *
    + *
  • {@link ElementKind#CLASS}
  • + *
  • {@link ElementKind#ENUM}
  • + *
  • {@link ElementKind#RECORD}
  • + *
+ * + *

This method serves as a convenience wrapper around {@link ElementKind#isClass()}. + * It also guards against null input by returning {@code false} when the argument is null. + * + *

Example Usage

+ *
{@code
+     * ElementKind classKind = ElementKind.CLASS;
+     * boolean result = isClass(classKind); // returns true
+     *
+     * ElementKind methodKind = ElementKind.METHOD;
+     * result = isClass(methodKind); // returns false
+     *
+     * result = isClass(null); // returns false
+     * }
+ * + * @param kind the ElementKind to check, may be null + * @return {@code true} if the kind is a class-like element, {@code false} otherwise + * @see ElementKind#isClass() + */ + static boolean isClass(ElementKind kind) { + return kind != null && kind.isClass(); + } + + /** + * Returns {@code true} if this is a kind of interface: + * either {@code INTERFACE} or {@code ANNOTATION_TYPE}. + * + *

+ * This method serves as a convenience wrapper around {@link ElementKind#isInterface()}. + * It also guards against null input by returning {@code false} when the argument is null. + *

+ * + *

Example Usage

+ *
{@code
+     * ElementKind interfaceKind = ElementKind.INTERFACE;
+     * boolean result = isInterface(interfaceKind); // returns true
+     *
+     * ElementKind annotationKind = ElementKind.ANNOTATION_TYPE;
+     * result = isInterface(annotationKind); // returns true
+     *
+     * ElementKind classKind = ElementKind.CLASS;
+     * result = isInterface(classKind); // returns false
+     *
+     * result = isInterface(null); // returns false
+     * }
+ * + * @param kind the ElementKind to check, may be null + * @return {@code true} if the kind is an interface-like element, {@code false} otherwise + * @see ElementKind#isInterface() + */ + static boolean isInterface(ElementKind kind) { + return kind != null && kind.isInterface(); + } + + /** + * Returns {@code true} if the specified {@link ElementKind} represents a declared type, + * which includes both {@linkplain #isClass(ElementKind) class-like} and + * {@linkplain #isInterface(ElementKind) interface-like} elements. + * + *

This method serves as a convenience wrapper that combines the checks for class and interface kinds. + * It also guards against null input by safely delegating to the respective methods. + * + *

Example Usage

+ *
{@code
+     * ElementKind classKind = ElementKind.CLASS;
+     * boolean result = isDeclaredType(classKind); // returns true
+     *
+     * ElementKind interfaceKind = ElementKind.INTERFACE;
+     * result = isDeclaredType(interfaceKind); // returns true
+     *
+     * ElementKind methodKind = ElementKind.METHOD;
+     * result = isDeclaredType(methodKind); // returns false
+     *
+     * result = isDeclaredType(null); // returns false
+     * }
+ * + * @param kind the ElementKind to check, may be null + * @return {@code true} if the kind is a declared type, {@code false} otherwise + * @see #isClass(ElementKind) + * @see #isInterface(ElementKind) + */ + static boolean isDeclaredType(ElementKind kind) { + return isClass(kind) || isInterface(kind); + } + + /** + * Returns {@code true} if this is a kind of field: + * either {@code FIELD} or {@code ENUM_CONSTANT}. + * + *

+ * This method serves as a convenience wrapper around {@link ElementKind#isField()}. + * It also guards against null input by returning {@code false} when the argument is null. + *

+ * + *

Example Usage

+ *
{@code
+     * ElementKind fieldKind = ElementKind.FIELD;
+     * boolean result = isField(fieldKind); // returns true
+     *
+     * ElementKind enumConstantKind = ElementKind.ENUM_CONSTANT;
+     * result = isField(enumConstantKind); // returns true
+     *
+     * ElementKind methodKind = ElementKind.METHOD;
+     * result = isField(methodKind); // returns false
+     *
+     * result = isField(null); // returns false
+     * }
+ * + * @param kind the ElementKind to check, may be null + * @return {@code true} if the kind is a field-like element, {@code false} otherwise + * @see ElementKind#isField() + */ + static boolean isField(ElementKind kind) { + return kind != null && kind.isField(); + } + + /** + * Returns {@code true} if this is a kind of executable: either + * {@code METHOD}, {@code CONSTRUCTOR}, {@code STATIC_INIT}, or + * {@code INSTANCE_INIT}. + * + *

This method serves as a convenience wrapper around {@link ElementKind#isExecutable()}. + * It also guards against null input by returning {@code false} when the argument is null. + * + *

Example Usage

+ *
{@code
+     * ElementKind methodKind = ElementKind.METHOD;
+     * boolean result = isExecutable(methodKind); // returns true
+     *
+     * ElementKind constructorKind = ElementKind.CONSTRUCTOR;
+     * result = isExecutable(constructorKind); // returns true
+     *
+     * ElementKind staticInitKind = ElementKind.STATIC_INIT;
+     * result = isExecutable(staticInitKind); // returns true
+     *
+     * ElementKind instanceInitKind = ElementKind.INSTANCE_INIT;
+     * result = isExecutable(instanceInitKind); // returns true
+     *
+     * ElementKind classKind = ElementKind.CLASS;
+     * result = isExecutable(classKind); // returns false
+     *
+     * result = isExecutable(null); // returns false
+     * }
+ * + * @param kind the ElementKind to check, may be null + * @return {@code true} if the kind is an executable-like element, {@code false} otherwise + * @see ElementKind#isExecutable() + */ + static boolean isExecutable(ElementKind kind) { + if (kind != null) { + switch (kind) { + case METHOD: + case CONSTRUCTOR: + case STATIC_INIT: + case INSTANCE_INIT: + return true; + } + } + return false; + } + + /** + * Returns {@code true} if the specified {@link ElementKind} represents a member element, + * which includes both {@linkplain #isField(ElementKind) field-like} and + * {@linkplain #isExecutable(ElementKind) executable-like} elements. + * + *

This method serves as a convenience wrapper that combines the checks for field and executable kinds. + * It also guards against null input by safely delegating to the respective methods. + * + *

Example Usage

+ *
{@code
+     * ElementKind fieldKind = ElementKind.FIELD;
+     * boolean result = isMember(fieldKind); // returns true
+     *
+     * ElementKind methodKind = ElementKind.METHOD;
+     * result = isMember(methodKind); // returns true
+     *
+     * ElementKind classKind = ElementKind.CLASS;
+     * result = isMember(classKind); // returns false
+     *
+     * result = isMember(null); // returns false
+     * }
+ * + * @param kind the ElementKind to check, may be null + * @return {@code true} if the kind is a member-like element, {@code false} otherwise + * @see #isField(ElementKind) + * @see #isExecutable(ElementKind) + */ + static boolean isMember(ElementKind kind) { + return isField(kind) || isExecutable(kind); + } + + /** + * Returns {@code true} if the specified {@link ElementKind} represents an initializer, + * either {@code STATIC_INIT} or {@code INSTANCE_INIT}. + * + *

+ * This method serves as a convenience wrapper around {@link ElementKind#isInitializer()}. + * It also guards against null input by returning {@code false} when the argument is null. + *

+ * + *

Example Usage

+ *
{@code
+     * ElementKind staticInitKind = ElementKind.STATIC_INIT;
+     * boolean result = isInitializer(staticInitKind); // returns true
+     *
+     * ElementKind instanceInitKind = ElementKind.INSTANCE_INIT;
+     * result = isInitializer(instanceInitKind); // returns true
+     *
+     * ElementKind methodKind = ElementKind.METHOD;
+     * result = isInitializer(methodKind); // returns false
+     *
+     * result = isInitializer(null); // returns false
+     * }
+ * + * @param kind the ElementKind to check, may be null + * @return {@code true} if the kind is an initializer-like element, {@code false} otherwise + * @see ElementKind#isInitializer() + */ + static boolean isInitializer(ElementKind kind) { + if (kind != null) { + switch (kind) { + case STATIC_INIT: + case INSTANCE_INIT: + return true; + } + } + return false; + } + + /** + * Returns {@code true} if the specified {@link ElementKind} represents a variable-like element, + * including: + *
    + *
  • {@link ElementKind#ENUM_CONSTANT}
  • + *
  • {@link ElementKind#FIELD}
  • + *
  • {@link ElementKind#PARAMETER}
  • + *
  • {@link ElementKind#LOCAL_VARIABLE}
  • + *
  • {@link ElementKind#EXCEPTION_PARAMETER}
  • + *
  • {@link ElementKind#RESOURCE_VARIABLE}
  • + *
  • {@link ElementKind#BINDING_VARIABLE}
  • + *
+ * + *

This method serves as a convenience wrapper around {@link ElementKind#isVariable()}. + * It also guards against null input by returning {@code false} when the argument is null. + * + *

Example Usage

+ *
{@code
+     * ElementKind enumConstantKind = ElementKind.ENUM_CONSTANT;
+     * boolean result = isVariable(enumConstantKind); // returns true
+     *
+     * ElementKind fieldKind = ElementKind.FIELD;
+     * result = isVariable(fieldKind); // returns true
+     *
+     * ElementKind parameterKind = ElementKind.PARAMETER;
+     * result = isVariable(parameterKind); // returns true
+     *
+     * ElementKind localVariableKind = ElementKind.LOCAL_VARIABLE;
+     * result = isVariable(localVariableKind); // returns true
+     *
+     * ElementKind exceptionParameterKind = ElementKind.EXCEPTION_PARAMETER;
+     * result = isVariable(exceptionParameterKind); // returns true
+     *
+     * ElementKind resourceVariableKind = ElementKind.RESOURCE_VARIABLE;
+     * result = isVariable(resourceVariableKind); // returns true
+     *
+     * ElementKind bindingVariableKind = ElementKind.BINDING_VARIABLE;
+     * result = isVariable(bindingVariableKind); // returns true
+     *
+     * ElementKind methodKind = ElementKind.METHOD;
+     * result = isVariable(methodKind); // returns false
+     *
+     * result = isVariable(null); // returns false
+     * }
+ * + * @param kind the ElementKind to check, may be null + * @return {@code true} if the kind is a variable-like element, {@code false} otherwise + * @see ElementKind#isVariable() + */ + static boolean isVariable(ElementKind kind) { + if (kind != null) { + switch (kind) { + case ENUM_CONSTANT: + case FIELD: + case PARAMETER: + case LOCAL_VARIABLE: + case EXCEPTION_PARAMETER: + case RESOURCE_VARIABLE: + return true; + } + // To be compatible with JDK 16 + return "BINDING_VARIABLE".equals(kind.name()); + } + return false; + } + + /** + * Converts the specified {@link ElementType} to an equivalent {@link ElementKind}. + * + *

+ * This method maps the provided {@link ElementType} to a corresponding {@link ElementKind}. + * If the provided {@code elementType} is {@code null}, this method returns {@link ElementKind#OTHER}. + *

+ * + *

Example Usage

+ *
{@code
+     * ElementType typeElement = ElementType.TYPE;
+     * ElementKind result = toElementKind(typeElement); // returns ElementKind.CLASS
+     *
+     * ElementType typeUseElement = ElementType.TYPE_USE;
+     * result = toElementKind(typeUseElement); // returns ElementKind.CLASS
+     *
+     * ElementType fieldElement = ElementType.FIELD;
+     * result = toElementKind(fieldElement); // returns ElementKind.FIELD
+     *
+     * result = toElementKind(null); // returns ElementKind.OTHER
+     * }
+ * + * @param elementType the ElementType to convert, may be {@code null} + * @return the corresponding ElementKind, never {@code null} + */ + static ElementKind toElementKind(ElementType elementType) { + if (elementType == null) { + return OTHER; + } + switch (elementType) { + case TYPE: + case TYPE_USE: + return CLASS; + default: + return valueOf(elementType.name()); + } + } + + /** + * Checks whether the specified {@link ElementKind} matches the specified {@link ElementType}. + * + *

+ * This method compares the provided {@link ElementKind} with the result of converting the + * {@link ElementType} to an equivalent {@link ElementKind} using {@link #toElementKind(ElementType)}. + * If either argument is null, the comparison will return {@code false}. + *

+ * + *

Example Usage

+ *
{@code
+     * ElementKind classKind = ElementKind.CLASS;
+     * ElementType typeElement = ElementType.TYPE;
+     * boolean result = matchesElementType(classKind, typeElement); // returns true
+     *
+     * ElementKind fieldKind = ElementKind.FIELD;
+     * ElementType typeUseElement = ElementType.TYPE_USE;
+     * result = matchesElementType(fieldKind, typeUseElement); // returns false
+     *
+     * result = matchesElementType(null, ElementType.TYPE); // returns false
+     * result = matchesElementType(ElementKind.CLASS, null); // returns false
+     * }
+ * + * @param elementKind the ElementKind to check, may be {@code null} + * @param elementType the ElementType to compare against, may be {@code null} + * @return {@code true} if the ElementKind matches the converted ElementType, {@code false} otherwise + * @see #toElementKind(ElementType) + */ + static boolean matchesElementType(ElementKind elementKind, ElementType elementType) { + return elementKind == toElementKind(elementType); + } + + /** + * Checks whether the specified {@link ElementKind} matches any of the specified {@link ElementType}s. + * + *

+ * This method converts each {@link ElementType} to its corresponding {@link ElementKind} + * using {@link #toElementKind(ElementType)}, and then compares it with the provided + * {@link ElementKind}. If any match is found, the method returns {@code true}. + *

+ * + *

Example Usage

+ *
{@code
+     * ElementKind classKind = ElementKind.CLASS;
+     * boolean result = matchesElementType(classKind, ElementType.TYPE, ElementType.METHOD); // returns true
+     *
+     * result = matchesElementType(classKind, ElementType.FIELD, ElementType.CONSTRUCTOR); // returns false
+     *
+     * result = matchesElementType(null, ElementType.TYPE); // returns false
+     *
+     * result = matchesElementType(ElementKind.FIELD, (ElementType[]) null); // returns false
+     * }
+ * + * @param elementKind the ElementKind to check, may be {@code null} + * @param elementTypes the array of ElementTypes to match against, may be {@code null} + * @return {@code true} if the ElementKind matches any of the converted ElementTypes, {@code false} otherwise + * @see #toElementKind(ElementType) + * @see #matchesElementType(ElementKind, ElementType) + */ + static boolean matchesElementType(ElementKind elementKind, ElementType... elementTypes) { + int length = length(elementTypes); + if (length < 1) { + return false; + } + for (int i = 0; i < length; i++) { + if (matchesElementType(elementKind, elementTypes[i])) { + return true; + } + } + return false; + } + + /** + * Checks whether the specified {@link Element} matches any of the specified {@link ElementType}s. + * + *

+ * This method determines if the provided {@link Element} has a kind that matches any of the + * converted {@link ElementKind} values derived from the given {@link ElementType}s. + * It delegates to {@link #matchesElementType(ElementKind, ElementType...)} for the actual comparison. + *

+ * + *

Example Usage

+ *
{@code
+     * Element element = ...; // an Element of kind CLASS
+     * boolean result = matchesElementType(element, ElementType.TYPE, ElementType.TYPE_USE); // returns true
+     *
+     * result = matchesElementType(element, ElementType.FIELD, ElementType.METHOD); // returns false
+     *
+     * result = matchesElementType(null, ElementType.TYPE); // returns false
+     *
+     * result = matchesElementType(element, (ElementType[]) null); // returns false
+     * }
+ * + * @param element the Element to check, may be {@code null} + * @param elementTypes the array of ElementTypes to match against, may be {@code null} + * @return {@code true} if the element matches any of the converted ElementTypes, {@code false} otherwise + * @see #matchesElementType(ElementKind, ElementType...) + */ + static boolean matchesElementType(Element element, ElementType... elementTypes) { + return element != null && matchesElementType(element.getKind(), elementTypes); + } + + /** + * Checks whether the specified {@link Element} matches the specified {@link ElementKind}. + * + *

+ * This method returns {@code false} if either the element or the kind is {@code null}. + * Otherwise, it compares the kind of the element with the provided {@link ElementKind}. + *

+ * + *

Example Usage

+ *
{@code
+     * Element element = ...; // an Element of kind METHOD
+     * ElementKind methodKind = ElementKind.METHOD;
+     * boolean result = matchesElementKind(element, methodKind); // returns true
+     *
+     * result = matchesElementKind(null, methodKind); // returns false
+     * result = matchesElementKind(element, null); // returns false
+     * }
+ * + * @param member the Element to check, may be {@code null} + * @param kind the ElementKind to match, may be {@code null} + * @return {@code true} if the element is not null and its kind matches the specified kind; otherwise, {@code false} + */ + static boolean matchesElementKind(Element member, ElementKind kind) { + return member == null || kind == null ? false : kind.equals(member.getKind()); + } + + /** + * Checks whether the specified {@link Element} is public and non-static. + * + *

This method verifies if the provided element has the {@link Modifier#PUBLIC} modifier + * and does not have the {@link Modifier#STATIC} modifier. + * + *

Example Usage

+ *
{@code
+     * Element methodElement = ...; // an Element with PUBLIC and non-STATIC modifiers
+     * boolean result = isPublicNonStatic(methodElement); // returns true
+     *
+     * Element staticMethodElement = ...; // an Element with PUBLIC and STATIC modifiers
+     * result = isPublicNonStatic(staticMethodElement); // returns false
+     *
+     * result = isPublicNonStatic(null); // returns false
+     * }
+ * + * @param member the Element to check, may be null + * @return {@code true} if the element is public and non-static; otherwise, {@code false} + */ + static boolean isPublicNonStatic(Element member) { + return hasModifiers(member, PUBLIC) && !hasModifiers(member, STATIC); + } + + /** + * Checks whether the specified {@link Element} has all of the specified {@link Modifier}s. + * + *

+ * This method returns {@code false} if the element is {@code null} or if the modifiers array is {@code null}. + * Otherwise, it verifies that the element contains all the provided modifiers. + *

+ * + *

Example Usage

+ *
{@code
+     * Element methodElement = ...; // an Element with PUBLIC and STATIC modifiers
+     * boolean result = hasModifiers(methodElement, Modifier.PUBLIC, Modifier.STATIC); // returns true
+     *
+     * result = hasModifiers(methodElement, Modifier.PRIVATE); // returns false
+     *
+     * result = hasModifiers(null, Modifier.PUBLIC); // returns false
+     *
+     * result = hasModifiers(methodElement, (Modifier[]) null); // returns false
+     * }
+ * + * @param member the {@link Element} to check, may be {@code null} + * @param modifiers the array of {@link Modifier}s to match, may be {@code null} + * @return {@code true} if the element is not null and contains all specified modifiers; otherwise, {@code false} + */ + static boolean hasModifiers(Element member, Modifier... modifiers) { + if (member == null || modifiers == null) { + return false; + } + Set actualModifiers = member.getModifiers(); + for (Modifier modifier : modifiers) { + if (!actualModifiers.contains(modifier)) { + return false; + } + } + return true; + } + + /** + * Filters the provided list of {@link Element} objects based on the given array of {@link Predicate} conditions. + * + *

This method applies a logical AND combination of all provided predicates to filter the elements. + * If the input list is empty or the predicates array is null or empty, an empty list is returned.

+ * + *

Example Usage

+ *
{@code
+     * List elements = ...; // a list of Elements
+     *
+     * // Filter public and static methods
+     * List filtered = filterElements(elements,
+     *     element -> ElementUtils.hasModifiers(element, Modifier.PUBLIC),
+     *     element -> ElementUtils.hasModifiers(element, Modifier.STATIC)
+     * );
+     *
+     * // Returns an empty list if no elements match
+     * filtered = filterElements(elements,
+     *     element -> ElementUtils.hasModifiers(element, Modifier.PRIVATE)
+     * );
+     *
+     * // Returns an empty list if input is null or predicates are null
+     * filtered = filterElements(null, (Predicate[]) null); // returns empty list
+     * }
+ * + * @param elements The list of elements to be filtered, may be {@code null} + * @param elementPredicates An array of predicates used to filter the elements, may be {@code null} + * @param The type of the elements, which must be a subclass of {@link Element} + * @return A filtered list of elements that match all the provided predicates. Returns an empty list if no elements match, + * or if the input list or predicate array is invalid. + */ + @Nonnull + @Immutable + static List filterElements(List elements, Predicate... elementPredicates) { + if (isEmpty(elements) || elementPredicates == null) { + return emptyList(); + } + if (isNotEmpty(elementPredicates)) { + Predicate predicate = and(elementPredicates); + elements = (List) elements.stream().filter(predicate).collect(toList()); + } + return elements.isEmpty() ? emptyList() : unmodifiableList(elements); + } + + /** + * Checks whether the parameter types of the given {@link ExecutableElement} match the specified {@link Type types}. + * + *

+ * This method compares the types of the parameters declared in the executable element with the provided expected types. + * It uses {@link TypeUtils#isSameType(Element, CharSequence)} to perform type comparison. + *

+ * + *

Example Usage

+ *
{@code
+     * ExecutableElement methodElement = ...; // an executable element with parameters
+     * boolean result = matchParameterTypes(methodElement, String.class, int.class); // returns true if parameter types match
+     *
+     * result = matchParameterTypes(null, String.class); // returns false
+     * result = matchParameterTypes(methodElement, (Type[]) null); // returns false
+     * }
+ * + * @param executableElement the executable element whose parameters are to be checked, may be {@code null} + * @param parameterTypes the expected parameter types, may be {@code null} + * @return {@code true} if the parameter types match; {@code false} otherwise + */ + static boolean matchParameterTypes(ExecutableElement executableElement, Type... parameterTypes) { + return executableElement == null || parameterTypes == null ? false : + matchParameterTypeNames(executableElement.getParameters(), getTypeNames(parameterTypes)); + } + + /** + * Checks whether the parameter types of the given list of {@link VariableElement} parameters match the specified {@link Type types}. + * + *

+ * This method compares each parameter's type with the corresponding expected type by their fully qualified type names. + * It uses {@link TypeUtils#isSameType(Element, CharSequence)} to perform the type comparison. + *

+ * + *

Example Usage

+ *
{@code
+     * List parameters = executableElement.getParameters();
+     * boolean result = matchParameterTypes(parameters, String.class, int.class); // returns true if types match
+     *
+     * result = matchParameterTypes(null, String.class); // returns false
+     * result = matchParameterTypes(parameters, (Type[]) null); // returns false
+     * }
+ * + * @param parameters the list of variable elements representing the parameters, may be {@code null} + * @param parameterTypes the expected parameter types, may be {@code null} + * @return {@code true} if all parameter types match their corresponding expected types; otherwise, {@code false} + */ + static boolean matchParameterTypes(List parameters, Type... parameterTypes) { + return parameters == null || parameterTypes == null ? false : matchParameterTypeNames(parameters, getTypeNames(parameterTypes)); + } + + /** + * Checks whether the parameter types of the given list of {@link VariableElement} parameters match the specified type names. + * + *

+ * This method compares each parameter's type with the corresponding expected type name using + * {@link TypeUtils#isSameType(Element, CharSequence)}. It ensures that both the number of parameters and their respective + * types match the provided array of type names. + *

+ * + *

Example Usage

+ *
{@code
+     * List parameters = executableElement.getParameters();
+     *
+     * // Check if the parameters match String and int
+     * boolean result = matchParameterTypeNames(parameters, "java.lang.String", "int"); // returns true if match
+     *
+     * // Returns false if either parameter list or type names are null
+     * result = matchParameterTypeNames(null, "java.lang.String"); // returns false
+     * result = matchParameterTypeNames(parameters, (CharSequence[]) null); // returns false
+     *
+     * // Returns false if the size of parameters and type names do not match
+     * result = matchParameterTypeNames(Arrays.asList(param1, param2), "java.lang.String"); // returns false
+     * }
+ * + * @param parameters the list of variable elements representing the parameters, may be {@code null} + * @param parameterTypeNames the expected fully qualified type names of the parameters, may be {@code null} + * @return {@code true} if all parameter types match their corresponding type names; otherwise, {@code false} + */ + static boolean matchParameterTypeNames(List parameters, CharSequence... parameterTypeNames) { + if (parameters == null || parameterTypeNames == null) { + return false; + } + + int length = length(parameterTypeNames); + int size = parameters.size(); + + if (size != length) { + return false; + } + + for (int i = 0; i < size; i++) { + VariableElement parameter = parameters.get(i); + if (!isSameType(parameter, parameterTypeNames[i])) { + return false; + } + } + return true; + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ExecutableElementComparator.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ExecutableElementComparator.java new file mode 100644 index 000000000..ead937db0 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ExecutableElementComparator.java @@ -0,0 +1,93 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.util.CharSequenceComparator; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import java.util.Comparator; +import java.util.List; + +/** + * The Comparator class for {@link ExecutableElement}, the comparison rule : + *
    + *
  1. Comparing to two {@link ExecutableElement#getSimpleName() element names} {@link String#compareTo(String) lexicographically}. + * If equals, go to step 2
  2. + *
  3. Comparing to the count of two parameters. If equals, go to step 3
  4. + *
  5. Comparing to the type names of parameters {@link String#compareTo(String) lexicographically}
  6. + *
+ * + *

Example Usage

+ *
+ * class Example {
+ *     void methodA() {}
+ *     void methodB() {}
+ *     void methodB(String param1) {}
+ *     void methodB(String param1, int param2) {}
+ * }
+ * 
+ * + *

When comparing methods:

+ *
    + *
  • {@code methodA} vs {@code methodB}: the names are compared lexicographically.
  • + *
  • {@code methodB()} vs {@code methodB(String)}: the number of parameters is compared.
  • + *
  • {@code methodB(String)} vs {@code methodB(String, int)}: the parameter type names are compared lexicographically.
  • + *
+ * + * @author Mercy + * @since 1.0.0 + */ +public class ExecutableElementComparator implements Comparator { + + /** + * The singleton instance + */ + public static final ExecutableElementComparator INSTANCE = new ExecutableElementComparator(); + + private ExecutableElementComparator() { + } + + @Override + public int compare(ExecutableElement e1, ExecutableElement e2) { + + if (e1.equals(e2)) { + return 0; + } + + // Step 1 + int value = CharSequenceComparator.INSTANCE.compare(e1.getSimpleName(), e2.getSimpleName()); + + if (value == 0) { // Step 2 + + List ps1 = e1.getParameters(); + List ps2 = e2.getParameters(); + + value = ps1.size() - ps2.size(); + + if (value == 0) { // Step 3 + for (int i = 0; i < ps1.size(); i++) { + value = CharSequenceComparator.INSTANCE.compare(ps1.get(i).asType().toString(), ps2.get(i).asType().toString()); + if (value != 0) { + break; + } + } + } + } + return value; + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/FieldUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/FieldUtils.java new file mode 100644 index 000000000..3ded2017e --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/FieldUtils.java @@ -0,0 +1,763 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.annotation.Immutable; +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.util.Utils; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import java.util.List; +import java.util.function.Predicate; + +import static io.microsphere.lang.model.util.ElementUtils.filterElements; +import static io.microsphere.lang.model.util.ElementUtils.hasModifiers; +import static io.microsphere.lang.model.util.ElementUtils.matchesElementKind; +import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; +import static io.microsphere.lang.model.util.TypeUtils.isEnumType; +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static io.microsphere.lang.function.Streams.filterFirst; +import static java.util.Collections.emptyList; +import static javax.lang.model.element.ElementKind.ENUM_CONSTANT; +import static javax.lang.model.element.ElementKind.FIELD; +import static javax.lang.model.element.Modifier.STATIC; +import static javax.lang.model.util.ElementFilter.fieldsIn; + +/** + * A utility interface for working with fields in the context of Java annotation processing. + *

+ * This interface provides a collection of static methods to retrieve, filter, and inspect + * {@link VariableElement fields} within a class or interface. It supports operations such as: + *

+ * + *
    + *
  • Retrieving fields by name
  • + *
  • Finding fields with specific modifiers (e.g., static, public)
  • + *
  • Filtering fields based on custom predicates
  • + *
  • Checking if a field is an enum constant or a non-static field
  • + *
  • Getting all declared or non-static fields from a type or element
  • + *
+ * + *

Example Usage

+ *
{@code
+ * // Get a specific field by name
+ * VariableElement field = FieldUtils.getDeclaredField(element, "myField");
+ *
+ * // Get all non-static fields
+ * List nonStaticFields = FieldUtils.getNonStaticFields(type);
+ *
+ * // Find a field by name in the current class and its superclasses
+ * VariableElement fieldInHierarchy = FieldUtils.findField(type, "myField");
+ *
+ * // Check if a field is non-static
+ * boolean isNonStatic = FieldUtils.isNonStaticField(field);
+ *
+ * // Get all fields, including those from superclasses
+ * List allFields = FieldUtils.getAllDeclaredFields(element);
+ * }
+ * + * @author
Mercy + * @since 1.0.0 + */ +public interface FieldUtils extends Utils { + + /** + * Retrieves the declared field with the specified name from the given element. + * + *

If the provided {@link Element} is null, this method returns null. It's typically used + * to retrieve a specific field from a class or interface element. + * + *

Example Usage

+ *
{@code
+     * Element element = ...; // A class or interface element
+     * String fieldName = "myField";
+     * VariableElement field = getDeclaredField(element, fieldName);
+     * if (field != null) {
+     *     System.out.println("Found field: " + field.getSimpleName());
+     * } else {
+     *     System.out.println("Field not found.");
+     * }
+     * }
+ * + * @param element the element to search for the field; if null, null is returned + * @param fieldName the name of the field to find + * @return the VariableElement representing the declared field, or null if not found + */ + @Nullable + static VariableElement getDeclaredField(Element element, String fieldName) { + return element == null ? null : getDeclaredField(element.asType(), fieldName); + } + + /** + * Retrieves the declared field with the specified name from the given type. + * + *

If the provided {@link TypeMirror} is null, this method returns null. + * It searches for a field with the exact specified name in the given type, + * and returns the first match found. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     * String fieldName = "myField";
+     * VariableElement field = getDeclaredField(type, fieldName);
+     * if (field != null) {
+     *     System.out.println("Found field: " + field.getSimpleName());
+     * } else {
+     *     System.out.println("Field not found.");
+     * }
+     * }
+ * + * @param type the type to search for the field; if null, null is returned + * @param fieldName the name of the field to find + * @return the VariableElement representing the declared field, or null if not found + */ + @Nullable + static VariableElement getDeclaredField(TypeMirror type, String fieldName) { + return filterFirst(findDeclaredFields(type, field -> fieldName.equals(field.getSimpleName().toString()))); + } + + /** + * Retrieves all declared fields from the given element without any filtering. + * + *

If the provided {@link Element} is null, this method returns an empty list. + * It's typically used to get all fields declared in a class or interface, + * excluding fields from superclasses or interfaces. + * + *

Example Usage

+ *
{@code
+     * Element element = ...; // A class or interface element
+     * List fields = getDeclaredFields(element);
+     * for (VariableElement field : fields) {
+     *     System.out.println("Field: " + field.getSimpleName());
+     * }
+     * }
+ * + * @param element the element to retrieve declared fields from + * @return a list of VariableElement objects representing the declared fields, + * or an empty list if the element is null or no fields are found + */ + @Nonnull + @Immutable + static List getDeclaredFields(Element element) { + return findDeclaredFields(element, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves all declared fields from the given type without any filtering. + * + *

If the provided {@link TypeMirror} is null, this method returns an empty list. + * It's typically used to get all fields declared directly within a class or interface, + * excluding fields from superclasses or interfaces. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     * List fields = getDeclaredFields(type);
+     * for (VariableElement field : fields) {
+     *     System.out.println("Declared field: " + field.getSimpleName());
+     * }
+     * }
+ * + * @param type the type to retrieve declared fields from + * @return a list of VariableElement objects representing the declared fields, + * or an empty list if the type is null or no fields are found + */ + @Nonnull + @Immutable + static List getDeclaredFields(TypeMirror type) { + return findDeclaredFields(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves all declared fields from the given element, including those from hierarchical types (e.g., superclasses), + * without any filtering. + * + *

If the provided {@link Element} is null, this method returns an empty list. It's typically used + * to get all fields declared directly within a class or interface, as well as those inherited from superclasses. + * + *

Example Usage

+ *
{@code
+     * Element element = ...; // A class or interface element
+     * List fields = getAllDeclaredFields(element);
+     * for (VariableElement field : fields) {
+     *     System.out.println("Declared field (including hierarchical): " + field.getSimpleName());
+     * }
+     * }
+ * + * @param element the element to retrieve all declared fields from + * @return a list of VariableElement objects representing all declared fields, + * or an empty list if the element is null or no fields are found + */ + @Nonnull + @Immutable + static List getAllDeclaredFields(Element element) { + return findAllDeclaredFields(element, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves all declared fields from the given type, including those from hierarchical types (e.g., superclasses), + * without any filtering. + * + *

If the provided {@link TypeMirror} is null, this method returns an empty list. It's typically used + * to get all fields declared directly within a class or interface, as well as those inherited from superclasses. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     * List fields = getAllDeclaredFields(type);
+     * for (VariableElement field : fields) {
+     *     System.out.println("Declared field (including hierarchical): " + field.getSimpleName());
+     * }
+     * }
+ * + * @param type the type to retrieve all declared fields from + * @return a list of VariableElement objects representing all declared fields, + * or an empty list if the type is null or no fields are found + */ + @Nonnull + @Immutable + static List getAllDeclaredFields(TypeMirror type) { + return findAllDeclaredFields(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves the first matching field with the specified name from the given element. + * + *

If the provided {@link Element} is null, this method returns null. + * It searches for a field with the exact specified name in the given element + * and returns the first match found. + * + *

Example Usage

+ *
{@code
+     * Element element = ...; // A valid class or interface element
+     * String fieldName = "myField";
+     * VariableElement field = findField(element, fieldName);
+     * if (field != null) {
+     *     System.out.println("Found field: " + field.getSimpleName());
+     * } else {
+     *     System.out.println("Field not found.");
+     * }
+     * }
+ * + * @param element the element to search for the field; if null, null is returned + * @param fieldName the name of the field to find + * @return the VariableElement representing the first matching field, or null if not found + */ + @Nullable + static VariableElement findField(Element element, String fieldName) { + return element == null ? null : findField(element.asType(), fieldName); + } + + /** + * Retrieves the first matching field with the specified name from the given type. + * + *

This method searches for a field with the exact specified name in the given type, + * including all hierarchical types (e.g., superclasses), and returns the first match found. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     * String fieldName = "myField";
+     * VariableElement field = findField(type, fieldName);
+     * if (field != null) {
+     *     System.out.println("Found field: " + field.getSimpleName());
+     * } else {
+     *     System.out.println("Field not found.");
+     * }
+     * }
+ * + * @param type the type to search for fields; if null, null is returned + * @param fieldName the name of the field to find + * @return the VariableElement representing the first matching field, or null if not found + */ + @Nullable + static VariableElement findField(TypeMirror type, String fieldName) { + return filterFirst(findAllDeclaredFields(type, field -> equalsFieldName(field, fieldName))); + } + + /** + * Retrieves the declared fields from the given element after applying the provided filters. + * + *

If the provided {@link Element} is null, this method returns an empty list. It searches + * for fields directly declared in the given element (excluding fields from superclasses or interfaces) + * and applies the specified filters to narrow down the results. + * + *

Example Usage

+ *
{@code
+     * Element element = ...; // A class or interface element
+     * List allFields = findDeclaredFields(element); // Get all declared fields
+     *
+     * // Get only non-static fields
+     * List nonStaticFields = findDeclaredFields(element, FieldUtils::isNonStaticField);
+     *
+     * // Get fields matching a specific name
+     * String fieldName = "myField";
+     * List matchingFields = findDeclaredFields(element, field -> fieldName.equals(field.getSimpleName().toString()));
+     *
+     * // Get fields with multiple filters (e.g., non-static and public)
+     * List publicNonStaticFields = findDeclaredFields(element,
+     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
+     *     FieldUtils::isNonStaticField
+     * );
+     * }
+ * + * @param element the element to retrieve declared fields from; if null, an empty list is returned + * @param fieldFilters the predicates used to filter the fields; optional + * @return a list of VariableElement objects representing the filtered declared fields + */ + @Nonnull + @Immutable + static List findDeclaredFields(Element element, Predicate... fieldFilters) { + return element == null ? emptyList() : findDeclaredFields(element.asType(), fieldFilters); + } + + /** + * Retrieves the declared fields from the given type after applying the provided filters. + * + *

This method searches for fields directly declared in the given type, + * excluding fields from superclasses or interfaces. It allows filtering + * the fields using the provided predicates to narrow down the results. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     *
+     * // Get all declared fields
+     * List allFields = findDeclaredFields(type);
+     *
+     * // Get only non-static fields
+     * List nonStaticFields = findDeclaredFields(type, FieldUtils::isNonStaticField);
+     *
+     * // Get fields matching a specific name
+     * String fieldName = "myField";
+     * List matchingFields = findDeclaredFields(type, field -> "myField".equals(field.getSimpleName().toString()));
+     *
+     * // Get fields with multiple filters (e.g., non-static and public)
+     * List publicNonStaticFields = findDeclaredFields(type,
+     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
+     *     FieldUtils::isNonStaticField
+     * );
+     * }
+ * + * @param type the type to retrieve declared fields from; if null, an empty list is returned + * @param fieldFilters the predicates used to filter the fields + * @return a list of VariableElement objects representing the filtered declared fields + */ + @Nonnull + @Immutable + static List findDeclaredFields(TypeMirror type, Predicate... fieldFilters) { + return filterDeclaredFields(type, false, fieldFilters); + } + + /** + * Retrieves all declared fields from the given element, including those from hierarchical types (e.g., superclasses), + * after applying the provided filters. + * + *

This method processes the given element and searches for all fields declared directly within it + * as well as those inherited from superclasses. The fields can be filtered using one or more predicates + * to narrow down the results. + * + *

Example Usage

+ *
{@code
+     * Element element = ...; // A valid class or interface element
+     *
+     * // Get all declared fields (including from superclasses)
+     * List allFields = findAllDeclaredFields(element);
+     *
+     * // Get only non-static fields
+     * List nonStaticFields = findAllDeclaredFields(element, FieldUtils::isNonStaticField);
+     *
+     * // Get fields matching a specific name
+     * String fieldName = "myField";
+     * List matchingFields = findAllDeclaredFields(element, field -> "myField".equals(field.getSimpleName().toString()));
+     *
+     * // Get fields with multiple filters (e.g., non-static and public)
+     * List publicNonStaticFields = findAllDeclaredFields(element,
+     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
+     *     FieldUtils::isNonStaticField
+     * );
+     * }
+ * + * @param element the element to retrieve all declared fields from; if null, an empty list is returned + * @param fieldFilters the predicates used to filter the fields; optional + * @return a list of VariableElement objects representing the filtered declared fields + */ + @Nonnull + @Immutable + static List findAllDeclaredFields(Element element, Predicate... fieldFilters) { + return element == null ? emptyList() : findAllDeclaredFields(element.asType(), fieldFilters); + } + + /** + * Retrieves all declared fields from the given type, including those from hierarchical types (e.g., superclasses), + * after applying the provided filters. + * + *

This method searches for fields directly declared in the given type as well as those inherited + * from superclasses. It allows filtering the fields using the provided predicates to narrow down the results. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     *
+     * // Get all declared fields (including hierarchical)
+     * List allFields = findAllDeclaredFields(type);
+     *
+     * // Get only non-static fields
+     * List nonStaticFields = findAllDeclaredFields(type, FieldUtils::isNonStaticField);
+     *
+     * // Get fields matching a specific name
+     * String fieldName = "myField";
+     * List matchingFields = findAllDeclaredFields(type, field -> "myField".equals(field.getSimpleName().toString()));
+     *
+     * // Get fields with multiple filters (e.g., non-static and public)
+     * List publicNonStaticFields = findAllDeclaredFields(type,
+     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
+     *     FieldUtils::isNonStaticField
+     * );
+     * }
+ * + * @param type the type to retrieve all declared fields from; if null, an empty list is returned + * @param fieldFilters the predicates used to filter the fields + * @return a list of VariableElement objects representing the filtered declared fields + */ + @Nonnull + @Immutable + static List findAllDeclaredFields(TypeMirror type, Predicate... fieldFilters) { + return filterDeclaredFields(type, true, fieldFilters); + } + + /** + * Filters and retrieves the declared fields from the given type based on the provided criteria. + * + *

This method is used to retrieve fields declared in the given type, optionally including fields + * from its superclasses or interfaces. The fields can be further filtered using one or more predicates + * to narrow down the results. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     *
+     * // Get all declared fields (excluding hierarchical)
+     * List declaredFields = filterDeclaredFields(type, false);
+     *
+     * // Get all declared fields including hierarchical ones
+     * List allFields = filterDeclaredFields(type, true);
+     *
+     * // Get only non-static fields
+     * List nonStaticFields = filterDeclaredFields(type, false, FieldUtils::isNonStaticField);
+     *
+     * // Get fields matching a specific name
+     * String fieldName = "myField";
+     * List matchingFields = filterDeclaredFields(type, false,
+     *     field -> "myField".equals(field.getSimpleName().toString()));
+     *
+     * // Get fields with multiple filters (e.g., non-static and public)
+     * List publicNonStaticFields = filterDeclaredFields(type, true,
+     *     field -> field.getModifiers().contains(Modifier.PUBLIC),
+     *     FieldUtils::isNonStaticField
+     * );
+     * }
+ * + * @param type the type to retrieve declared fields from; if null, an empty list is returned + * @param includeHierarchicalTypes whether to include fields from hierarchical types (e.g., superclasses) + * @param fieldFilters the predicates used to filter the fields; optional + * @return a list of VariableElement objects representing the filtered declared fields + */ + @Nonnull + @Immutable + static List filterDeclaredFields(TypeMirror type, boolean includeHierarchicalTypes, Predicate... fieldFilters) { + if (type == null) { + return emptyList(); + } + + List declaredMembers = getDeclaredMembers(type, includeHierarchicalTypes); + if (isEmpty(declaredMembers)) { + return emptyList(); + } + + List fields = fieldsIn(declaredMembers); + + return filterElements(fields, fieldFilters); + } + + /** + * Determines whether the given field is an enum member field. + * + *

An enum member field is typically a public static final field that represents + * a constant within an enum declaration. This method checks if the field's enclosing + * element is an enum and if the field's kind matches {@link ElementKind#ENUM_CONSTANT}. + * + *

Example Usage

+ *
{@code
+     * VariableElement field = ...; // A valid field element
+     * boolean isEnumMember = FieldUtils.isEnumMemberField(field);
+     * if (isEnumMember) {
+     *     System.out.println("The field is an enum member field.");
+     * } else {
+     *     System.out.println("The field is not an enum member field.");
+     * }
+     * }
+ * + * @param field the field to check; may be null + * @return true if the field is an enum member field, false otherwise + */ + static boolean isEnumMemberField(VariableElement field) { + if (field == null || !isEnumType(field.getEnclosingElement())) { + return false; + } + return ENUM_CONSTANT.equals(field.getKind()); + } + + /** + * Checks if the given field is a non-static field. + * + *

This method verifies whether the provided {@link VariableElement} represents a field + * that is not declared with the {@code static} modifier. It first checks if the element is + * a valid field (including enum constants) using the {@link #isField(VariableElement)} method, + * and then ensures that the field does not have the static modifier. + * + *

Example Usage

+ *
{@code
+     * VariableElement field = ...; // A valid field element
+     * boolean isNonStatic = FieldUtils.isNonStaticField(field);
+     * if (isNonStatic) {
+     *     System.out.println("The field is non-static.");
+     * } else {
+     *     System.out.println("The field is static or not a valid field.");
+     * }
+     * }
+ * + * @param field the VariableElement to check; may be null + * @return true if the field is a valid field (as per {@link #isField(VariableElement)}) + * and does not have the 'static' modifier, false otherwise + */ + static boolean isNonStaticField(VariableElement field) { + return isField(field) && !hasModifiers(field, STATIC); + } + + /** + * Checks if the given element is a field or an enum constant. + * + *

This method determines whether the provided {@link VariableElement} represents a valid field + * or an enum constant. It returns {@code true} if the element's kind is either + * {@link ElementKind#FIELD} or {@link ElementKind#ENUM_CONSTANT}. + * + *

Example Usage

+ *
{@code
+     * VariableElement field = ...; // A valid field element
+     * boolean isValidField = FieldUtils.isField(field);
+     * if (isValidField) {
+     *     System.out.println("The element is a valid field or enum constant.");
+     * } else {
+     *     System.out.println("The element is not a field or enum constant.");
+     * }
+     * }
+ * + * @param field the VariableElement to check; may be null + * @return true if the element is a field ({@link javax.lang.model.element.ElementKind#FIELD}) + * or an enum constant ({@link javax.lang.model.element.ElementKind#ENUM_CONSTANT}), false otherwise + */ + static boolean isField(VariableElement field) { + return matchesElementKind(field, FIELD) || isEnumMemberField(field); + } + + /** + * Checks if the given element is a field or an enum constant, and also has all the specified modifiers. + * + *

This method extends the {@link #isField(VariableElement)} method by additionally verifying that + * the field has all of the specified modifiers. It returns {@code true} only if the element is a valid field + * (including enum constants) and contains all the provided modifiers. + * + *

Example Usage

+ *
{@code
+     * VariableElement field = ...; // A valid field element
+     *
+     * // Check if it's a public static field
+     * boolean isPublicStaticField = FieldUtils.isField(field, Modifier.PUBLIC, Modifier.STATIC);
+     * if (isPublicStaticField) {
+     *     System.out.println("The field is a public static field.");
+     * } else {
+     *     System.out.println("The field is not a public static field.");
+     * }
+     *
+     * // Check if it's a private field
+     * boolean isPrivateField = FieldUtils.isField(field, Modifier.PRIVATE);
+     * if (isPrivateField) {
+     *     System.out.println("The field is a private field.");
+     * } else {
+     *     System.out.println("The field is not a private field.");
+     * }
+     * }
+ * + * @param field the VariableElement to check; may be null + * @param modifiers the modifiers to match (e.g., Modifier.PUBLIC, Modifier.STATIC) + * @return true if the element is a field ({@link javax.lang.model.element.ElementKind#FIELD}) + * or an enum constant ({@link javax.lang.model.element.ElementKind#ENUM_CONSTANT}), + * and it has all of the specified modifiers, false otherwise + */ + static boolean isField(VariableElement field, Modifier... modifiers) { + return isField(field) && hasModifiers(field, modifiers); + } + + /** + * Retrieves all declared non-static fields from the given type. + * + *

This method returns a list of fields that are declared directly within the given type + * and are not marked as static. It does not include fields from superclasses or interfaces. + * If the provided {@link TypeMirror} is null, this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     * List nonStaticFields = FieldUtils.getNonStaticFields(type);
+     * for (VariableElement field : nonStaticFields) {
+     *     System.out.println("Non-static field: " + field.getSimpleName());
+     * }
+     * }
+ * + * @param type the type to search for non-static fields; if null, an empty list is returned + * @return a list of VariableElement objects representing the non-static fields, + * or an empty list if the type is null or no non-static fields are found + */ + @Nonnull + @Immutable + static List getNonStaticFields(TypeMirror type) { + return findDeclaredFields(type, FieldUtils::isNonStaticField); + } + + /** + * Retrieves all declared non-static fields from the given element. + * + *

This method processes the provided element and retrieves all fields directly declared + * within it that are not marked as static. It excludes fields from superclasses or interfaces. + * If the provided element is null, this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * Element element = ...; // A valid class or interface element
+     * List nonStaticFields = FieldUtils.getNonStaticFields(element);
+     * for (VariableElement field : nonStaticFields) {
+     *     System.out.println("Non-static field: " + field.getSimpleName());
+     * }
+     *
+     * // Handling null case
+     * List safeList = FieldUtils.getNonStaticFields(null);
+     * System.out.println(safeList.isEmpty()); // true
+     * }
+ * + * @param element the element to search for non-static fields; if null, an empty list is returned + * @return a list of VariableElement objects representing the non-static fields, + * or an empty list if the element is null or no non-static fields are found + */ + @Nonnull + @Immutable + static List getNonStaticFields(Element element) { + return element == null ? emptyList() : getNonStaticFields(element.asType()); + } + + /** + * Retrieves all non-static fields from the given type, including those from hierarchical types (e.g., superclasses). + * + *

This method searches for all fields declared directly within the given type, as well as those inherited + * from its superclasses or interfaces, and filters out only the non-static fields. If the provided {@link TypeMirror} + * is null, this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = element.asType(); // A valid type from an element
+     * List nonStaticFields = FieldUtils.getAllNonStaticFields(type);
+     * for (VariableElement field : nonStaticFields) {
+     *     System.out.println("Non-static field (including hierarchical): " + field.getSimpleName());
+     * }
+     *
+     * // Handling null case
+     * List safeList = FieldUtils.getAllNonStaticFields(null);
+     * System.out.println(safeList.isEmpty()); // true
+     * }
+ * + * @param type the type to retrieve all non-static fields from; if null, an empty list is returned + * @return a list of VariableElement objects representing all non-static fields, + * or an empty list if the type is null or no non-static fields are found + */ + @Nonnull + @Immutable + static List getAllNonStaticFields(TypeMirror type) { + return findAllDeclaredFields(type, FieldUtils::isNonStaticField); + } + + /** + * Retrieves all non-static fields from the given element, including those from superclasses and interfaces. + * + *

This method processes the provided element and retrieves all fields declared directly within it, + * as well as those inherited from superclasses or interfaces. It filters out only the non-static fields. + * If the provided element is null, this method returns an empty list. + * + *

Example Usage

+ *
{@code
+     * Element element = ...; // A valid class or interface element
+     * List nonStaticFields = FieldUtils.getAllNonStaticFields(element);
+     * for (VariableElement field : nonStaticFields) {
+     *     System.out.println("Non-static field: " + field.getSimpleName());
+     * }
+     *
+     * // Handling null case
+     * List safeList = FieldUtils.getAllNonStaticFields(null);
+     * System.out.println(safeList.isEmpty()); // true
+     * }
+ * + * @param element the element to retrieve all non-static fields from; if null, an empty list is returned + * @return a list of VariableElement objects representing all non-static fields, + * or an empty list if the element is null or no non-static fields are found + */ + @Nonnull + @Immutable + static List getAllNonStaticFields(Element element) { + return element == null ? emptyList() : getAllNonStaticFields(element.asType()); + } + + /** + * Checks if the simple name of the given field matches the specified field name. + * + *

This method ensures both the {@link VariableElement} and the field name are non-null + * before comparing their string representations for equality. + * + *

Example Usage

+ *
{@code
+     * VariableElement field = ...; // A valid field element
+     * CharSequence fieldName = "myField";
+     * boolean isMatch = equalsFieldName(field, fieldName);
+     * if (isMatch) {
+     *     System.out.println("Field name matches: " + fieldName);
+     * } else {
+     *     System.out.println("Field name does not match.");
+     * }
+     * }
+ * + * @param field the VariableElement representing the field; may be null + * @param fieldName the CharSequence representing the expected field name; may be null + * @return true if both the field and fieldName are non-null and their string representations match, false otherwise + */ + static boolean equalsFieldName(VariableElement field, CharSequence fieldName) { + return field != null && fieldName != null && field.getSimpleName().toString().equals(fieldName.toString()); + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitor.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitor.java new file mode 100644 index 000000000..01b074447 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitor.java @@ -0,0 +1,196 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleAnnotationValueVisitor6; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import static io.microsphere.constants.SymbolConstants.COMMA_CHAR; +import static io.microsphere.constants.SymbolConstants.LEFT_CURLY_BRACE_CHAR; +import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET_CHAR; +import static io.microsphere.constants.SymbolConstants.RIGHT_CURLY_BRACE_CHAR; +import static io.microsphere.constants.SymbolConstants.RIGHT_SQUARE_BRACKET_CHAR; +import static io.microsphere.json.JSONUtils.append; +import static io.microsphere.json.JSONUtils.appendName; +import static io.microsphere.lang.model.util.AnnotationUtils.getAttributeName; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValues; +import static io.microsphere.lang.model.util.TypeUtils.getTypeName; + +/** + * A visitor implementation for converting {@link AnnotationValue} objects into JSON-formatted strings. + * This class extends {@link SimpleAnnotationValueVisitor6} and is designed to work with Java annotation + * processing tools to generate JSON representations of annotation values. + * + *

Example Usage

+ *
{@code
+ * // Example 1: Visiting a simple annotation value
+ * AnnotationValue value = ...; // e.g., a String, int, or boolean value
+ * ExecutableElement method = ...; // the method corresponding to the annotation attribute
+ * StringBuilder jsonBuilder = new StringBuilder();
+ * JSONAnnotationValueVisitor visitor = new JSONAnnotationValueVisitor(jsonBuilder);
+ * visitor.visit(value, method); // Result: appends JSON key-value pair to jsonBuilder
+ *
+ * // Example 2: Visiting an array annotation value
+ * List arrayValues = ...; // list of annotation values
+ * visitor.visitArray(arrayValues, method); // Result: appends JSON array to jsonBuilder
+ *
+ * // Example 3: Visiting a nested annotation
+ * AnnotationMirror annotationMirror = ...;
+ * visitor.visitAnnotation(annotationMirror, method); // Result: appends JSON object to jsonBuilder
+ * }
+ * + *

This visitor is typically used during annotation processing to serialize annotation values + * into a structured JSON format, useful for configuration or metadata generation purposes. + * + * @author Mercy + * @see SimpleAnnotationValueVisitor6 + * @since 1.0.0 + */ +public class JSONAnnotationValueVisitor extends SimpleAnnotationValueVisitor6 { + + private final StringBuilder jsonBuilder; + + public JSONAnnotationValueVisitor(StringBuilder jsonBuilder) { + super(jsonBuilder); + this.jsonBuilder = jsonBuilder; + } + + @Override + public StringBuilder visitBoolean(boolean value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitByte(byte value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitChar(char value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitDouble(double value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitFloat(float value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitInt(int value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitLong(long value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitShort(short value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitString(String value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value); + return jsonBuilder; + } + + @Override + public StringBuilder visitType(TypeMirror value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), getTypeName(value)); + return jsonBuilder; + } + + @Override + public StringBuilder visitEnumConstant(VariableElement value, ExecutableElement attributeMethod) { + append(jsonBuilder, getAttributeName(attributeMethod), value.getSimpleName().toString()); + return jsonBuilder; + } + + @Override + public StringBuilder visitAnnotation(AnnotationMirror value, ExecutableElement attributeMethod) { + Map elementValues = getElementValues(value); + Iterator> iterator = elementValues.entrySet().iterator(); + StringBuilder annotationJsonBuilder = new StringBuilder(); + annotationJsonBuilder.append(LEFT_CURLY_BRACE_CHAR); + JSONAnnotationValueVisitor visitor = new JSONAnnotationValueVisitor(annotationJsonBuilder); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + AnnotationValue annotationValue = entry.getValue(); + visitor.visit(annotationValue, entry.getKey()); + if (iterator.hasNext()) { + annotationJsonBuilder.append(COMMA_CHAR); + } + } + annotationJsonBuilder.append(RIGHT_CURLY_BRACE_CHAR); + + return doAppend(attributeMethod, annotationJsonBuilder); + } + + @Override + public StringBuilder visitArray(List values, ExecutableElement attributeMethod) { + StringBuilder arrayJsonBuilder = new StringBuilder(); + + arrayJsonBuilder.append(LEFT_SQUARE_BRACKET_CHAR); + int size = values.size(); + JSONAnnotationValueVisitor visitor = new JSONAnnotationValueVisitor(arrayJsonBuilder); + for (int i = 0; i < size; i++) { + AnnotationValue annotationValue = values.get(i); + annotationValue.accept(visitor, null); + if (i < size - 1) { + arrayJsonBuilder.append(COMMA_CHAR); + } + } + arrayJsonBuilder.append(RIGHT_SQUARE_BRACKET_CHAR); + + return doAppend(attributeMethod, arrayJsonBuilder); + } + + @Override + public StringBuilder visitUnknown(AnnotationValue annotationValue, ExecutableElement attributeMethod) { + return jsonBuilder; + } + + protected StringBuilder doAppend(ExecutableElement attributeMethod, StringBuilder value) { + appendName(this.jsonBuilder, getAttributeName(attributeMethod)) + .append(value); + return this.jsonBuilder; + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java new file mode 100644 index 000000000..36a20126c --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java @@ -0,0 +1,221 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementVisitor; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.AbstractElementVisitor6; +import javax.lang.model.util.ElementKindVisitor6; +import java.util.List; + +import static java.lang.Boolean.FALSE; + +/** + * A specialized {@link ElementVisitor} implementation that traverses Java elements and generates JSON + * representations of the elements' metadata. + * + *

This abstract class extends {@link ElementKindVisitor6} to provide a foundation for creating + * JSON-based representations of various Java language elements such as packages, types, methods, + * fields, and type parameters. + * + *

Example Usage

+ *
{@code
+ * public class CustomJSONElementVisitor extends JSONElementVisitor {
+ *     // Implement specific visit methods to customize JSON generation
+ * }
+ * }
+ * + *

Subclasses should implement or override the appropriate visit methods to customize how + * different Java elements are represented in JSON. + * + * @author Mercy + * @see ElementVisitor + * @see ElementKindVisitor6 + * @see AbstractElementVisitor6 + * @since 1.0.0 + */ +public abstract class JSONElementVisitor extends ElementKindVisitor6 { + + public JSONElementVisitor() { + super(FALSE); + } + + @Override + public final Boolean visitPackage(PackageElement e, StringBuilder jsonBuilder) { + return supportsPackage(e) && doVisitPackage(e, jsonBuilder); + } + + @Override + public final Boolean visitVariable(VariableElement e, StringBuilder stringBuilder) { + return supportsVariable(e) && super.visitVariable(e, stringBuilder); + } + + @Override + public final Boolean visitExecutable(ExecutableElement e, StringBuilder jsonBuilder) { + return supportsExecutable(e) && super.visitExecutable(e, jsonBuilder); + } + + @Override + public final Boolean visitType(TypeElement e, StringBuilder jsonBuilder) { + boolean appended = false; + if (supportsType(e) && super.visitType(e, jsonBuilder)) { + appended = true; + } + + // The declared members of the type element + if (visitMembers(e.getEnclosedElements(), jsonBuilder)) { + appended = true; + } + + return appended; + } + + @Override + public final Boolean visitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { + if (!supports(e)) { + return FALSE; + } + + boolean appended = false; + if (supportsTypeParameter(e) && doVisitTypeParameter(e, jsonBuilder)) { + appended = true; + } + + // The declared members of the type element + if (visitMembers(e.getEnclosedElements(), jsonBuilder)) { + appended = true; + } + + return appended; + } + + protected boolean visitMembers(List members, StringBuilder jsonBuilder) { + boolean appended = false; + for (Element member : members) { + if (member.accept(this, jsonBuilder)) { + appended = true; + } + } + return appended; + } + + /** + * Determines whether the specified element is supported for processing. + * + *

This method can be overridden to provide custom logic for deciding + * if an element should be processed by this visitor.

+ * + * @param e the element to check + * @return {@code true} if the element is supported; {@code false} otherwise + */ + protected boolean supports(Element e) { + return true; + } + + /** + * Determines whether the specified package element is supported for processing. + * + *

This method can be overridden to provide custom logic for deciding + * if a package element should be processed by this visitor.

+ * + * @param e the package element to check + * @return {@code true} if the package element is supported; {@code false} otherwise + */ + protected boolean supportsPackage(PackageElement e) { + return supports(e); + } + + /** + * Determines whether the specified variable element is supported for processing. + * + *

This method can be overridden to provide custom logic for deciding + * if a variable element should be processed by this visitor.

+ * + * @param e the variable element to check + * @return {@code true} if the variable element is supported; {@code false} otherwise + */ + protected boolean supportsVariable(VariableElement e) { + return supports(e); + } + + /** + * Determines whether the specified executable element is supported for processing. + * + *

This method can be overridden to provide custom logic for deciding + * if an executable element should be processed by this visitor.

+ * + * @param e the executable element to check + * @return {@code true} if the executable element is supported; {@code false} otherwise + */ + protected boolean supportsExecutable(ExecutableElement e) { + return supports(e); + } + + /** + * Determines whether the specified type element is supported for processing. + * + *

This method can be overridden to provide custom logic for deciding + * if a type element should be processed by this visitor.

+ * + * @param e the type element to check + * @return {@code true} if the type element is supported; {@code false} otherwise + */ + protected boolean supportsType(TypeElement e) { + return supports(e); + } + + /** + * Determines whether the specified type parameter element is supported for processing. + * + *

This method can be overridden to provide custom logic for deciding + * if a type parameter element should be processed by this visitor.

+ * + * @param e the type parameter element to check + * @return {@code true} if the type parameter element is supported; {@code false} otherwise + */ + protected boolean supportsTypeParameter(TypeParameterElement e) { + return supports(e); + } + + /** + * Visits a package element and appends its JSON representation to the builder. + * + * @param e the package element to visit + * @param jsonBuilder the string builder used to construct the JSON output + * @return {@code true} if any content was appended; {@code false} otherwise + */ + protected boolean doVisitPackage(PackageElement e, StringBuilder jsonBuilder) { + return super.visitPackage(e, jsonBuilder); + } + + /** + * Visits a type parameter element and appends its JSON representation to the builder. + * + * @param e the type parameter element to visit + * @param jsonBuilder the string builder used to construct the JSON output + * @return {@code true} if any content was appended; {@code false} otherwise + */ + protected boolean doVisitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { + return super.visitTypeParameter(e, jsonBuilder); + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/LoggerUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/LoggerUtils.java new file mode 100644 index 000000000..4fed09a7f --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/LoggerUtils.java @@ -0,0 +1,54 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + + +import io.microsphere.logging.Logger; +import io.microsphere.util.Utils; + +import static io.microsphere.logging.LoggerFactory.getLogger; + +/** + * Logger Utils + * + * @author Mercy + * @since 1.0.0 + */ +public interface LoggerUtils extends Utils { + + Logger LOGGER = getLogger("microsphere-lang-model"); + + static void trace(String format, Object... args) { + LOGGER.trace(format, args); + } + + static void debug(String format, Object... args) { + LOGGER.debug(format, args); + } + + static void info(String format, Object... args) { + LOGGER.info(format, args); + } + + static void warn(String format, Object... args) { + LOGGER.warn(format, args); + } + + static void error(String format, Object... args) { + LOGGER.error(format, args); + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MemberUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MemberUtils.java new file mode 100644 index 000000000..addc57158 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MemberUtils.java @@ -0,0 +1,423 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.annotation.Immutable; +import io.microsphere.annotation.Nonnull; +import io.microsphere.util.Utils; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; + +import static io.microsphere.lang.model.util.ElementUtils.filterElements; +import static io.microsphere.lang.model.util.TypeUtils.getAllDeclaredTypes; +import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; + +/** + * A utility interface for handling members (such as fields, methods, constructors) from types + * in the {@link javax.lang.model} package. + * + *

Overview

+ *

+ * This interface provides a set of static methods to retrieve and filter members of a type, + * including direct members and those inherited from superclasses and interfaces. + * It is designed to simplify the process of working with type elements and their enclosed elements + * during annotation processing. + *

+ * + *

Key Features

+ *
    + *
  • {@link #getDeclaredMembers(TypeMirror) getDeclaredMembers(TypeMirror)} - Retrieves directly declared members of a type.
  • + *
  • {@link #getAllDeclaredMembers(TypeMirror) getAllDeclaredMembers(TypeMirror)} - Retrieves all declared members, including those from superclasses and interfaces.
  • + *
  • {@link #findDeclaredMembers(TypeMirror, Predicate...) findDeclaredMembers(TypeMirror, Predicate...)} - Retrieves and filters declared members based on provided predicates.
  • + *
  • {@link #findAllDeclaredMembers(TypeMirror, Predicate...) findAllDeclaredMembers(TypeMirror, Predicate...)} - Retrieves all declared members and applies filtering via predicates.
  • + *
+ * + *

Example Usage

+ *
{@code
+ * // Retrieve all declared methods from a TypeMirror
+ * List methods = findDeclaredMembers(typeMirror,
+ *     element -> element.getKind() == ElementKind.METHOD
+ * );
+ * }
+ * + *
{@code
+ * // Retrieve all fields that start with "m_"
+ * List filteredFields = findDeclaredMembers(typeElement,
+ *     element -> element.getKind() == ElementKind.FIELD,
+ *     element -> element.getSimpleName().toString().startsWith("m_")
+ * );
+ * }
+ * + * @author
Mercy + * @since 1.0.0 + */ +public interface MemberUtils extends Utils { + + /** + * Returns the directly declared members of the provided {@link TypeMirror}. + * If the given type is {@code null}, an empty list will be returned. + * + *

Example Usage

+ *
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
+     * List members = getDeclaredMembers(typeMirror);
+     * if (!members.isEmpty()) {
+     *     for (Element member : members) {
+     *         System.out.println("Declared member: " + member);
+     *     }
+     * }
+     * 
+ * + * @param type the type mirror to retrieve declared members from + * @return a list of directly declared members, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List getDeclaredMembers(TypeMirror type) { + return type == null ? emptyList() : getDeclaredMembers(ofTypeElement(type)); + } + + /** + * Returns the directly declared members of the provided {@link TypeElement}. + * If the given type is {@code null}, an empty list will be returned. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
+     * List members = getDeclaredMembers(typeElement);
+     * if (!members.isEmpty()) {
+     *     for (Element member : members) {
+     *         System.out.println("Declared member: " + member);
+     *     }
+     * }
+     * }
+ * + * @param type the type element to retrieve declared members from + * @return a list of directly declared members, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List getDeclaredMembers(TypeElement type) { + return type == null ? emptyList() : findDeclaredMembers(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Returns all declared members (including those from superclasses and interfaces) of the provided {@link TypeMirror}. + * If the given type is {@code null}, an empty list will be returned. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
+     * List allMembers = getAllDeclaredMembers(typeMirror);
+     * if (!allMembers.isEmpty()) {
+     *     for (Element member : allMembers) {
+     *         System.out.println("All declared member: " + member);
+     *     }
+     * }
+     * }
+ * + * @param type the type mirror to retrieve all declared members from + * @return a list of all declared members, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List getAllDeclaredMembers(TypeMirror type) { + return type == null ? emptyList() : findAllDeclaredMembers(ofTypeElement(type), EMPTY_PREDICATE_ARRAY); + } + + /** + * Returns all declared members (including those from superclasses and interfaces) of the provided {@link TypeElement}. + * If the given type is {@code null}, an empty list will be returned. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
+     * List allMembers = getAllDeclaredMembers(typeElement);
+     * if (!allMembers.isEmpty()) {
+     *     for (Element member : allMembers) {
+     *         System.out.println("All declared member: " + member);
+     *     }
+     * }
+     * }
+ * + * @param type the type element to retrieve all declared members from + * @return a list of all declared members, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List getAllDeclaredMembers(TypeElement type) { + return type == null ? emptyList() : findAllDeclaredMembers(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Returns the declared members of the provided {@link TypeMirror}, optionally including + * members from superclasses and interfaces. + * + *

If the given type is {@code null}, an empty list will be returned.

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
+     * boolean includeSuperMembers = true; // Include members from superclasses/interfaces
+     * List members = getDeclaredMembers(typeMirror, includeSuperMembers);
+     * for (Element member : members) {
+     *     System.out.println("Member: " + member);
+     * }
+     * }
+ * + * @param type the type mirror to retrieve declared members from + * @param includeHierarchicalTypes whether to include members from superclasses and interfaces + * @return a list of declared members, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List getDeclaredMembers(TypeMirror type, boolean includeHierarchicalTypes) { + return includeHierarchicalTypes ? getAllDeclaredMembers(type) : getDeclaredMembers(type); + } + + /** + * Returns the declared members of the provided {@link TypeElement}, optionally including + * members from superclasses and interfaces. + * + *

If the given type is {@code null}, an empty list will be returned.

+ * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
+     * boolean includeSuperMembers = true; // Include members from superclasses/interfaces
+     * List members = getDeclaredMembers(typeElement, includeSuperMembers);
+     * for (Element member : members) {
+     *     System.out.println("Member: " + member);
+     * }
+     * }
+ * + * @param type the type element to retrieve declared members from + * @param includeHierarchicalTypes whether to include members from superclasses and interfaces + * @return a list of declared members, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List getDeclaredMembers(TypeElement type, boolean includeHierarchicalTypes) { + return includeHierarchicalTypes ? getAllDeclaredMembers(type) : getDeclaredMembers(type); + } + + /** + * Returns the declared members of the provided {@link TypeMirror}, optionally filtered by one or more predicates. + * + *

If the given type is {@code null}, an empty list will be returned.

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
+     * List methods = findDeclaredMembers(typeMirror,
+     *     element -> element.getKind() == ElementKind.METHOD,
+     *     element -> element.getSimpleName().toString().startsWith("get")
+     * );
+     * for (Element method : methods) {
+     *     System.out.println("Matching method: " + method);
+     * }
+     * }
+ * + * @param the type of elements to filter + * @param type the type mirror to retrieve declared members from + * @param memberFilters the predicates used to filter members + * @return a list of declared members matching the filters, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List findDeclaredMembers(TypeMirror type, Predicate... memberFilters) { + return type == null ? emptyList() : findDeclaredMembers(ofTypeElement(type), memberFilters); + } + + /** + * Returns the directly declared members of the provided {@link TypeElement}, optionally filtered by one or more predicates. + * + *

If the given type is {@code null}, an empty list will be returned.

+ * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
+     * List fields = findDeclaredMembers(typeElement,
+     *     element -> element.getKind() == ElementKind.FIELD,
+     *     element -> element.getSimpleName().toString().startsWith("m_")
+     * );
+     * for (Element field : fields) {
+     *     System.out.println("Matching field: " + field);
+     * }
+     * }
+ * + * @param the type of elements to filter + * @param type the type element to retrieve declared members from + * @param memberFilters the predicates used to filter members + * @return a list of declared members matching the filters, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List findDeclaredMembers(TypeElement type, Predicate... memberFilters) { + if (type == null) { + return emptyList(); + } + return filterElements((List) type.getEnclosedElements(), memberFilters); + } + + /** + * Returns all declared members (including those from superclasses and interfaces) of the provided {@link TypeMirror}, + * optionally filtered by one or more predicates. + * + *

If the given type is {@code null}, an empty list will be returned.

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
+     * List methods = findAllDeclaredMembers(typeMirror,
+     *     element -> element.getKind() == ElementKind.METHOD,
+     *     element -> element.getSimpleName().toString().startsWith("get")
+     * );
+     * for (Element method : methods) {
+     *     System.out.println("Matching method: " + method);
+     * }
+     * }
+ * + * @param the type of elements to filter + * @param type the type mirror to retrieve all declared members from + * @param memberFilters the predicates used to filter members + * @return a list of all declared members matching the filters, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List findAllDeclaredMembers(TypeMirror type, Predicate... memberFilters) { + return type == null ? emptyList() : findAllDeclaredMembers(ofTypeElement(type), memberFilters); + } + + /** + * Retrieves all declared members (fields, methods, constructors, etc.) from the given {@link TypeElement}, + * including those inherited from superclasses and implemented interfaces. + * + *

This method collects all declared members by traversing the type hierarchy and applying + * the provided filters to narrow down the results. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
+     * List fields = findAllDeclaredMembers(typeElement,
+     *     element -> element.getKind() == ElementKind.FIELD
+     * );
+     * for (Element field : fields) {
+     *     System.out.println("Field: " + field);
+     * }
+     * }
+ * + *
{@code
+     * List methods = findAllDeclaredMembers(typeElement,
+     *     element -> element.getKind() == ElementKind.METHOD,
+     *     element -> element.getSimpleName().toString().startsWith("get")
+     * );
+     * for (Element method : methods) {
+     *     System.out.println("Getter method: " + method);
+     * }
+     * }
+ * + * @param the type of elements to filter + * @param type the type element to retrieve all declared members from + * @param memberFilters the predicates used to filter members + * @return a list of all declared members matching the filters, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List findAllDeclaredMembers(TypeElement type, Predicate... memberFilters) { + if (type == null) { + return emptyList(); + } + List declaredMembers = (List) getAllDeclaredTypes(type) + .stream() + .map(MemberUtils::getDeclaredMembers) + .flatMap(Collection::stream) + .collect(toList()); + return filterElements(declaredMembers, memberFilters); + } + + /** + * Returns the declared members of the provided {@link TypeMirror}, optionally including + * members from superclasses and interfaces, and filtered by one or more predicates. + * + *

If the given type is {@code null}, an empty list will be returned.

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror instance
+     * boolean includeSuperMembers = true; // Include members from superclasses/interfaces
+     * List methods = findDeclaredMembers(typeMirror, includeSuperMembers,
+     *     element -> element.getKind() == ElementKind.METHOD,
+     *     element -> element.getSimpleName().toString().startsWith("get")
+     * );
+     * for (Element method : methods) {
+     *     System.out.println("Matching method: " + method);
+     * }
+     * }
+ * + * @param the type of elements to filter + * @param type the type mirror to retrieve declared members from + * @param includeHierarchicalTypes whether to include members from superclasses and interfaces + * @param memberFilters the predicates used to filter members + * @return a list of declared members matching the filters, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List findDeclaredMembers(TypeMirror type, boolean includeHierarchicalTypes, Predicate... memberFilters) { + return includeHierarchicalTypes ? findAllDeclaredMembers(type, memberFilters) : findDeclaredMembers(type, memberFilters); + } + + /** + * Returns the declared members of the provided {@link TypeElement}, optionally including + * members from superclasses and interfaces, and filtered by one or more predicates. + * + *

If the given type is {@code null}, an empty list will be returned.

+ * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement instance
+     * boolean includeSuperMembers = true; // Include members from superclasses/interfaces
+     * List fields = findDeclaredMembers(typeElement, includeSuperMembers,
+     *     element -> element.getKind() == ElementKind.FIELD,
+     *     element -> element.getSimpleName().toString().startsWith("m_")
+     * );
+     * for (Element field : fields) {
+     *     System.out.println("Matching field: " + field);
+     * }
+     * }
+ * + * @param the type of elements to filter + * @param type the type element to retrieve declared members from + * @param all whether to include members from superclasses and interfaces + * @param memberFilters the predicates used to filter members + * @return a list of declared members matching the filters, or an empty list if the type is {@code null} + */ + @Nonnull + @Immutable + static List findDeclaredMembers(TypeElement type, boolean all, Predicate... memberFilters) { + return all ? findAllDeclaredMembers(type, memberFilters) : findDeclaredMembers(type, memberFilters); + } + +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MessagerUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MessagerUtils.java new file mode 100644 index 000000000..63ad95180 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MessagerUtils.java @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional inpatternion 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. + */ + +package io.microsphere.lang.model.util; + +import io.microsphere.util.Utils; + +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.tools.Diagnostic.Kind; + +import static io.microsphere.lang.model.util.LoggerUtils.debug; +import static io.microsphere.lang.model.util.LoggerUtils.error; +import static io.microsphere.lang.model.util.LoggerUtils.info; +import static io.microsphere.lang.model.util.LoggerUtils.warn; +import static io.microsphere.text.FormatUtils.format; +import static javax.tools.Diagnostic.Kind.ERROR; +import static javax.tools.Diagnostic.Kind.MANDATORY_WARNING; +import static javax.tools.Diagnostic.Kind.NOTE; +import static javax.tools.Diagnostic.Kind.WARNING; + +/** + * {@link Messager} utilities class + * + * @author Mercy + * @see Messager + * @since 1.0.0 + */ +public interface MessagerUtils extends Utils { + + /** + * Prints a note message using the {@link ProcessingEnvironment}'s {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and delegates to the underlying {@link Messager} obtained from the processing environment. + *

+ * + *

Example Usage

+ *
+     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
+     * printNote(processingEnv, "Found {0} elements matching criteria", count);
+     * 
+ * + * @param processingEnv the processing environment to obtain the messager from + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printNote(ProcessingEnvironment processingEnv, String pattern, Object... args) { + printNote(processingEnv.getMessager(), pattern, args); + } + + /** + * Prints a note message using the provided {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and forwards it to the underlying {@link Messager}. + *

+ * + *

Example Usage

+ *
+     * Messager messager = processingEnv.getMessager();
+     * printNote(messager, "Found {0} elements matching criteria", count);
+     * 
+ * + * @param messager the messager to use for printing the message + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + /** + * Prints a note message using the provided {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and forwards it to the underlying {@link Messager}. + *

+ * + *

Example Usage

+ *
+     * Messager messager = processingEnv.getMessager();
+     * printNote(messager, "Found {0} elements matching criteria", count);
+     * 
+ * + * @param messager the messager to use for printing the message + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printNote(Messager messager, String pattern, Object... args) { + printMessage(messager, NOTE, pattern, args); + } + + /** + * Prints a warning message using the {@link ProcessingEnvironment}'s {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and delegates to the underlying {@link Messager} obtained from the processing environment. + * The message will be logged at the warning level through both the {@link Messager} + * and internal logging utilities. + *

+ * + *

Example Usage

+ *
+     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
+     * printWarning(processingEnv, "Found {0} deprecated elements", count);
+     * 
+ * + * @param processingEnv the processing environment to obtain the messager from + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printWarning(ProcessingEnvironment processingEnv, String pattern, Object... args) { + printWarning(processingEnv.getMessager(), pattern, args); + } + + /** + * Prints a warning message using the provided {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and forwards it to the underlying {@link Messager} as a warning level message. + * The message will also be logged through internal logging utilities at the warning level. + *

+ * + *

Example Usage

+ *
+     * Messager messager = processingEnv.getMessager();
+     * printWarning(messager, "Found {0} deprecated elements", count);
+     * 
+ * + * @param messager the messager to use for printing the message + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printWarning(Messager messager, String pattern, Object... args) { + printMessage(messager, WARNING, pattern, args); + } + + /** + * Prints a mandatory warning message using the {@link ProcessingEnvironment}'s {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and delegates to the underlying {@link Messager} obtained from the processing environment. + * The message will be logged at the warning level through both the {@link Messager} + * and internal logging utilities. + *

+ * + *

Example Usage

+ *
+     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
+     * printMandatoryWarning(processingEnv, "Found {0} obsolete elements", count);
+     * 
+ * + * @param processingEnv the processing environment to obtain the messager from + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printMandatoryWarning(ProcessingEnvironment processingEnv, String pattern, Object... args) { + printMandatoryWarning(processingEnv.getMessager(), pattern, args); + } + + /** + * Prints a mandatory warning message using the provided {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and forwards it to the underlying {@link Messager} as a mandatory warning level message. + * The message will also be logged through internal logging utilities at the warning level. + *

+ * + *

Example Usage

+ *
+     * Messager messager = processingEnv.getMessager();
+     * printMandatoryWarning(messager, "Found {0} obsolete elements", count);
+     * 
+ * + * @param messager the messager to use for printing the message + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printMandatoryWarning(Messager messager, String pattern, Object... args) { + printMessage(messager, MANDATORY_WARNING, pattern, args); + } + + /** + * Prints an error message using the {@link ProcessingEnvironment}'s {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and delegates to the underlying {@link Messager} obtained from the processing environment. + * The message will be logged at the error level through both the {@link Messager} + * and internal logging utilities. + *

+ * + *

Example Usage

+ *
+     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
+     * printError(processingEnv, "Failed to process {0} elements", count);
+     * 
+ * + * @param processingEnv the processing environment to obtain the messager from + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printError(ProcessingEnvironment processingEnv, String pattern, Object... args) { + printError(processingEnv.getMessager(), pattern, args); + } + + /** + * Prints an error message using the provided {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * and forwards it to the underlying {@link Messager} as an error level message. + * The message will also be logged through internal logging utilities at the error level. + *

+ * + *

Example Usage

+ *
+     * Messager messager = processingEnv.getMessager();
+     * printError(messager, "Failed to process {0} elements", count);
+     * 
+ * + * @param messager the messager to use for printing the message + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printError(Messager messager, String pattern, Object... args) { + printMessage(messager, ERROR, pattern, args); + } + + /** + * Prints a message of the specified kind using the {@link ProcessingEnvironment}'s {@link Messager}. + * + *

+ * This method retrieves the {@link Messager} from the provided {@link ProcessingEnvironment} + * and delegates to the {@link #printMessage(Messager, Kind, String, Object...)} method + * to handle message formatting and output. + *

+ * + *

Example Usage

+ *
+     * ProcessingEnvironment processingEnv = ...; // Obtain processing environment
+     * printMessage(processingEnv, Kind.WARNING, "Found {0} deprecated elements", count);
+     * 
+ * + * @param processingEnv the processing environment to obtain the messager from + * @param kind the kind of message to print (e.g., error, warning, note) + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printMessage(ProcessingEnvironment processingEnv, Kind kind, String pattern, Object... args) { + printMessage(processingEnv.getMessager(), kind, pattern, args); + } + + /** + * Prints a message of the specified kind using the provided {@link Messager}. + * + *

+ * This method formats the message using the provided pattern and arguments, + * sends it to the annotation processor's messager, and also logs it using + * internal logging utilities at the appropriate level. + *

+ * + *

Example Usage

+ *
+     * Messager messager = processingEnv.getMessager();
+     * printMessage(messager, Kind.ERROR, "Failed to process {0} elements", count);
+     * 
+ * + * @param messager the messager to use for printing the message + * @param kind the kind of message to print (e.g., error, warning, note) + * @param pattern the message pattern to format (supports {@link String#format} syntax) + * @param args the arguments for the message pattern + */ + static void printMessage(Messager messager, Kind kind, String pattern, Object... args) { + String message = format(pattern, args); + messager.printMessage(kind, message); + switch (kind) { + case ERROR: + error(pattern, args); + break; + case WARNING: + case MANDATORY_WARNING: + warn(pattern, args); + break; + case NOTE: + info(pattern, args); + break; + default: + debug(pattern, args); + } + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java new file mode 100644 index 000000000..925d3ee37 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java @@ -0,0 +1,1165 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + + +import io.microsphere.annotation.Immutable; +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.util.Utils; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; + +import static io.microsphere.lang.model.util.ElementUtils.filterElements; +import static io.microsphere.lang.model.util.ElementUtils.isPublicNonStatic; +import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypeNames; +import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypes; +import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; +import static io.microsphere.lang.model.util.TypeUtils.isSameType; +import static io.microsphere.lang.model.util.TypeUtils.ofDeclaredType; +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static io.microsphere.lang.function.Predicates.and; +import static io.microsphere.lang.function.Streams.filterFirst; +import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; +import static io.microsphere.util.ArrayUtils.EMPTY_TYPE_ARRAY; +import static io.microsphere.util.ArrayUtils.isNotEmpty; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static java.util.stream.Collectors.toList; +import static javax.lang.model.element.ElementKind.METHOD; +import static javax.lang.model.util.ElementFilter.methodsIn; + +/** + * The utilities class for method in the package "javax.lang.model." + * + * @author Mercy + * @since 1.0.0 + */ +public interface MethodUtils extends Utils { + + /** + * Gets all declared methods of the specified {@link TypeElement}. + * + *

This method returns a list of methods directly declared in the given type element, + * excluding inherited methods. If the provided type element is {@code null}, an empty list is returned. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * List methods = getDeclaredMethods(typeElement);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type element, may be null + * @return a list of executable elements representing all declared methods of the specified type, + * or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List getDeclaredMethods(TypeElement type) { + return findDeclaredMethods(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Gets all declared methods of the specified {@link TypeMirror}. + * + *

This method returns a list of methods directly declared in the given type mirror, + * excluding inherited methods. If the provided type mirror is {@code null}, an empty list is returned. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * List methods = getDeclaredMethods(typeMirror);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type mirror, may be null + * @return a list of executable elements representing all declared methods of the specified type, + * or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List getDeclaredMethods(TypeMirror type) { + return findDeclaredMethods(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Get all declared methods of the specified type element, including those inherited from superclasses and interfaces. + * + *

This method returns a list of methods directly declared in the given type element, including those + * inherited from superclasses and interfaces. If the provided type element is {@code null}, an empty list is returned. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * List methods = getAllDeclaredMethods(typeElement);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type element, may be null + * @return a list of executable elements representing all declared methods of the specified type, + * including those inherited from superclasses and interfaces, or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List getAllDeclaredMethods(TypeElement type) { + return findAllDeclaredMethods(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Get all declared methods of the specified type mirror, including those inherited from superclasses and interfaces. + * + *

This method returns a list of methods directly declared in the given type mirror, including those + * inherited from superclasses and interfaces. If the provided type mirror is {@code null}, an empty list is returned. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * List methods = getAllDeclaredMethods(typeMirror);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type mirror, may be null + * @return a list of executable elements representing all declared methods of the specified type, + * including those inherited from superclasses and interfaces, or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List getAllDeclaredMethods(TypeMirror type) { + return findAllDeclaredMethods(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Find the declared methods of the specified {@link TypeElement}. + * + *

This method returns a list of methods directly declared in the given type element, + * excluding inherited methods. If the provided type element is {@code null}, an empty list is returned. + * Additional filters can be applied to narrow down the list of methods based on custom criteria. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * List methods = findDeclaredMethods(typeElement);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method: " + method.getSimpleName());
+     * }
+     * }
+ * + *

Filtering usage example: + *

{@code
+     * List publicNonStaticMethods = findDeclaredMethods(typeElement, MethodUtils::isPublicNonStaticMethod);
+     * for (ExecutableElement method : publicNonStaticMethods) {
+     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type element, may be null + * @param methodFilters the filters for method elements + * @return a list of executable elements representing all declared methods of the specified type, + * or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List findDeclaredMethods(TypeElement type, Predicate... methodFilters) { + return type == null ? emptyList() : findDeclaredMethods(type.asType(), methodFilters); + } + + /** + * Find the declared methods of the specified type mirror. + * + *

This method returns a list of methods directly declared in the given type mirror, + * excluding inherited methods. If the provided type mirror is {@code null}, an empty list is returned. + * Additional filters can be applied to narrow down the list of methods based on custom criteria. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * List methods = findDeclaredMethods(typeMirror);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method: " + method.getSimpleName());
+     * }
+     * }
+ * + *

Filtering usage example: + *

{@code
+     * List publicNonStaticMethods = findDeclaredMethods(typeMirror, MethodUtils::isPublicNonStaticMethod);
+     * for (ExecutableElement method : publicNonStaticMethods) {
+     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type mirror, may be null + * @param methodFilters the filters for method elements + * @return a list of executable elements representing all declared methods of the specified type, + * or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List findDeclaredMethods(TypeMirror type, Predicate... methodFilters) { + return filterDeclaredMethods(type, false, methodFilters); + } + + /** + * Find all declared methods of the specified type element, including those inherited from superclasses and interfaces, + * and exclude methods declared in the specified excluded types. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * List methods = findAllDeclaredMethods(typeElement);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
+     * }
+     * }
+ * + *

Excluding methods from specific types: + *

{@code
+     * List methodsExcludingObject = findAllDeclaredMethods(typeElement, Object.class);
+     * for (ExecutableElement method : methodsExcludingObject) {
+     *     System.out.println("Method excluding Object: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type element, may be null + * @param excludedTypes the types whose methods should be excluded from the result, optional + * @return a list of executable elements representing all declared methods of the specified type, + * including those inherited from superclasses and interfaces, but excluding those declared + * in the excluded types, or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List findAllDeclaredMethods(TypeElement type, Type... excludedTypes) { + return type == null ? emptyList() : findAllDeclaredMethods(type.asType(), excludedTypes); + } + + /** + * Find all declared methods of the specified type mirror, including those inherited from superclasses and interfaces, + * and exclude methods declared in the specified excluded types. + * + *

This method returns a list of methods directly declared in the given type mirror, + * including those inherited from superclasses and interfaces. If the provided type mirror is {@code null}, + * an empty list is returned. Additional filtering can be applied to exclude methods declared in specific types. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * List methods = findAllDeclaredMethods(typeMirror);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
+     * }
+     * }
+ * + *

Excluding methods from specific types: + *

{@code
+     * List methodsExcludingObject = findAllDeclaredMethods(typeMirror, Object.class);
+     * for (ExecutableElement method : methodsExcludingObject) {
+     *     System.out.println("Method excluding Object: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type mirror, may be null + * @param excludedTypes the types whose methods should be excluded from the result, optional + * @return a list of executable elements representing all declared methods of the specified type, + * excluding those declared in the excluded types, or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List findAllDeclaredMethods(TypeMirror type, Type... excludedTypes) { + if (type == null) { + return emptyList(); + } + return findAllDeclaredMethods(type, methodPredicateForExcludedTypes(excludedTypes)); + } + + /** + * Finds all public non-static methods declared in the specified {@link TypeElement}, excluding those inherited from superclasses or interfaces, + * and optionally excludes methods declared in the specified excluded types. + * + *

This method returns a list of executable elements representing public non-static methods directly declared in the given type element. + * If the provided type element is {@code null}, an empty list is returned. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * List methods = findPublicNonStaticMethods(typeElement);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
+     * }
+     * }
+ * + *

Excluding methods from specific types: + *

{@code
+     * List methodsExcludingObject = findPublicNonStaticMethods(typeElement, Object.class);
+     * for (ExecutableElement method : methodsExcludingObject) {
+     *     System.out.println("Public Non-Static Method (excluding Object): " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type element, may be null + * @param excludedTypes the types whose methods should be excluded from the result, optional + * @return a list of executable elements representing all public non-static methods declared in the specified type, + * or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List findPublicNonStaticMethods(TypeElement type, Type... excludedTypes) { + return type == null ? emptyList() : findPublicNonStaticMethods(ofDeclaredType(type), excludedTypes); + } + + /** + * Find all public non-static methods declared in the specified type mirror, excluding those inherited from superclasses or interfaces, + * and optionally exclude methods declared in the specified excluded types. + * + *

This method returns a list of executable elements representing public non-static methods directly declared in the given type. + * If the provided type is {@code null}, an empty list is returned. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * List methods = findPublicNonStaticMethods(typeMirror);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
+     * }
+     * }
+ * + *

Excluding methods from specific types: + *

{@code
+     * List methodsExcludingObject = findPublicNonStaticMethods(typeMirror, Object.class);
+     * for (ExecutableElement method : methodsExcludingObject) {
+     *     System.out.println("Public Non-Static Method (excluding Object): " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type mirror, may be null + * @param excludedTypes the types whose methods should be excluded from the result, optional + * @return a list of executable elements representing all public non-static methods declared in the specified type, + * excluding those declared in the excluded types, or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List findPublicNonStaticMethods(TypeMirror type, Type... excludedTypes) { + if (type == null) { + return emptyList(); + } + + Predicate predicate = and(methodPredicateForExcludedTypes(excludedTypes), MethodUtils::isPublicNonStaticMethod); + + return findAllDeclaredMethods(type, predicate); + } + + /** + * Find all declared methods of the specified {@link TypeElement}, including those inherited from superclasses and interfaces, + * and optionally filter them using the provided predicates. + * + *

This method returns a list of methods directly declared in the given type element, + * including those inherited from superclasses and interfaces. If the provided type element is {@code null}, + * an empty list is returned. Additional filters can be applied to narrow down the list of methods based on custom criteria. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * List methods = findAllDeclaredMethods(typeElement);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
+     * }
+     * }
+ * + *

Filtering usage example: + *

{@code
+     * List publicNonStaticMethods = findAllDeclaredMethods(typeElement, MethodUtils::isPublicNonStaticMethod);
+     * for (ExecutableElement method : publicNonStaticMethods) {
+     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type element, may be null + * @param methodFilters the filters for method elements, optional + * @return a list of executable elements representing all declared methods of the specified type, + * including those inherited from superclasses and interfaces, or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List findAllDeclaredMethods(TypeElement type, Predicate... methodFilters) { + return type == null ? emptyList() : findAllDeclaredMethods(type.asType(), methodFilters); + } + + /** + * Finds all declared methods of the specified type mirror, including those inherited from superclasses and interfaces, + * and optionally filters them using the provided predicates. + * + *

This method returns a list of methods directly declared in the given type mirror, including those + * inherited from superclasses and interfaces. If the provided type mirror is {@code null}, an empty list is returned. + * Additional filters can be applied to narrow down the list of methods based on custom criteria. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * List methods = findAllDeclaredMethods(typeMirror);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method (including inherited): " + method.getSimpleName());
+     * }
+     * }
+ * + *

Filtering usage example: + *

{@code
+     * List publicNonStaticMethods = findAllDeclaredMethods(typeMirror, MethodUtils::isPublicNonStaticMethod);
+     * for (ExecutableElement method : publicNonStaticMethods) {
+     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the specified type mirror, may be null + * @param methodFilters the filters for method elements, optional + * @return a list of executable elements representing all declared methods of the specified type, + * including those inherited from superclasses and interfaces, or an empty list if the input type is null + */ + @Nonnull + @Immutable + static List findAllDeclaredMethods(TypeMirror type, Predicate... methodFilters) { + return filterDeclaredMethods(type, true, methodFilters); + } + + /** + * Filters the declared methods of the specified type based on the given predicates. + * + *

This method returns a list of methods directly declared in the given type, + * optionally including those inherited from superclasses and interfaces. If the provided type is {@code null}, + * an empty list is returned. Additional filters can be applied to narrow down the list of methods based on custom criteria. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * List methods = filterDeclaredMethods(typeMirror, false);
+     * for (ExecutableElement method : methods) {
+     *     System.out.println("Declared Method: " + method.getSimpleName());
+     * }
+     * }
+ * + *

Filtering usage example: + *

{@code
+     * List publicNonStaticMethods = filterDeclaredMethods(typeMirror, false, MethodUtils::isPublicNonStaticMethod);
+     * for (ExecutableElement method : publicNonStaticMethods) {
+     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the type whose declared methods are to be filtered, may be null + * @param includeHierarchicalTypes whether to include methods from superclasses and interfaces + * @param methodFilters the predicates used to filter the methods, optional + * @return a list of executable elements representing the filtered methods, + * or an empty list if the input type is null or no methods match the filters + */ + @Nonnull + @Immutable + static List filterDeclaredMethods(TypeMirror type, boolean includeHierarchicalTypes, Predicate... methodFilters) { + if (type == null) { + return emptyList(); + } + + List declaredMembers = getDeclaredMembers(type, includeHierarchicalTypes); + if (isEmpty(declaredMembers)) { + return emptyList(); + } + + List methods = methodsIn(declaredMembers); + + return filterElements(methods, methodFilters); + } + + /** + * Checks if the given executable element is a method. + * + *

This method determines whether the provided executable element represents a method. + * If the element is {@code null}, the method returns {@code false}. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement executableElement = ...; // Obtain a valid ExecutableElement
+     * boolean isMethod = MethodUtils.isMethod(executableElement);
+     * if (isMethod) {
+     *     System.out.println("The element is a method.");
+     * } else {
+     *     System.out.println("The element is not a method.");
+     * }
+     * }
+ * + * @param method the executable element to check, may be null + * @return true if the element is a method, false otherwise + */ + static boolean isMethod(ExecutableElement method) { + return method != null && METHOD.equals(method.getKind()); + } + + /** + * Checks whether the given method is a public non-static method. + * + *

This method verifies if the provided executable element represents a method that is both public and non-static. + * If the method is {@code null}, the method returns {@code false}. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
+     * boolean isPublicNonStatic = MethodUtils.isPublicNonStaticMethod(method);
+     * if (isPublicNonStatic) {
+     *     System.out.println("The method is a public non-static method.");
+     * } else {
+     *     System.out.println("The method is not a public non-static method.");
+     * }
+     * }
+ * + * @param method the executable element to check, may be null + * @return true if the method is a public non-static method, false otherwise + */ + static boolean isPublicNonStaticMethod(ExecutableElement method) { + return isMethod(method) && isPublicNonStatic(method); + } + + /** + * Finds a method with the specified name in the given type element, using an empty parameter type array as default. + * + *

This method searches for a method with the specified name in the given type element. + * If no method with the specified name is found, it returns {@code null}. + * The search considers methods declared directly in the type, excluding inherited methods. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * String methodName = "myMethod";
+     * ExecutableElement method = MethodUtils.findMethod(typeElement, methodName);
+     *
+     * if (method != null) {
+     *     System.out.println("Found method: " + method.getSimpleName());
+     * } else {
+     *     System.out.println("Method not found.");
+     * }
+     * }
+ * + * @param type the type element to search for the method + * @param methodName the name of the method to find + * @return the first matching executable element representing the method, or null if none is found + */ + @Nullable + static ExecutableElement findMethod(TypeElement type, String methodName) { + return findMethod(type, methodName, EMPTY_TYPE_ARRAY); + } + + /** + * Finds a method with the specified name in the given type mirror, using an empty parameter type array as default. + * + *

This method searches for a method with the specified name in the given type mirror. + * If no method with the specified name is found, it returns {@code null}. + * The search considers methods declared directly in the type, excluding inherited methods. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * String methodName = "myMethod";
+     * ExecutableElement method = MethodUtils.findMethod(typeMirror, methodName);
+     *
+     * if (method != null) {
+     *     System.out.println("Found method: " + method.getSimpleName());
+     * } else {
+     *     System.out.println("Method not found.");
+     * }
+     * }
+ * + * @param type the type mirror to search for the method + * @param methodName the name of the method to find + * @return the first matching executable element representing the method, or null if none is found + */ + @Nullable + static ExecutableElement findMethod(TypeMirror type, String methodName) { + return findMethod(type, methodName, EMPTY_TYPE_ARRAY); + } + + /** + * Finds a method with the specified name and parameter types in the given {@link TypeElement}. + * + *

This method searches for a method with the specified name and exact parameter types + * directly declared in the provided type element, excluding inherited methods. + * If no matching method is found, it returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * String methodName = "myMethod";
+     * Type[] parameterTypes = new Type[] { String.class, int.class };
+     * ExecutableElement method = MethodUtils.findMethod(typeElement, methodName, parameterTypes);
+     *
+     * if (method != null) {
+     *     System.out.println("Found method: " + method.getSimpleName());
+     * } else {
+     *     System.out.println("Method not found.");
+     * }
+     * }
+ * + * @param type the specified type element, may be null + * @param methodName the name of the method to find, must not be null + * @param parameterTypes the parameter types of the method to match, must not be null + * @return the first matching executable element representing the method, or null if none is found + */ + @Nullable + static ExecutableElement findMethod(TypeElement type, String methodName, Type... parameterTypes) { + return type == null ? null : findMethod(type.asType(), methodName, parameterTypes); + } + + /** + * Finds a method with the specified name and parameter types in the given type mirror. + * + *

This method searches for a method with the specified name and exact parameter types + * directly declared in the provided type mirror, excluding inherited methods. + * If no matching method is found, it returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * String methodName = "myMethod";
+     * Type[] parameterTypes = new Type[] { String.class, int.class };
+     * ExecutableElement method = MethodUtils.findMethod(typeMirror, methodName, parameterTypes);
+     *
+     * if (method != null) {
+     *     System.out.println("Found method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the type mirror to search for the method, may be null + * @param methodName the name of the method to find, must not be null + * @param parameterTypes the parameter types of the method to match, must not be null + * @return the first matching executable element representing the method, or null if none is found + */ + @Nullable + static ExecutableElement findMethod(TypeMirror type, String methodName, Type... parameterTypes) { + if (type == null || methodName == null || parameterTypes == null) { + return null; + } + List allDeclaredMethods = findAllDeclaredMethods(type, method -> matches(method, methodName, parameterTypes)); + return allDeclaredMethods.isEmpty() ? null : allDeclaredMethods.get(0); + } + + /** + * Finds a method with the specified name and parameter type names in the given {@link TypeElement}. + * + *

This method searches for a method with the specified name and exact parameter type names + * directly declared in the provided type element, excluding inherited methods. + * If no matching method is found, it returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * TypeElement typeElement = ...; // Obtain a valid TypeElement
+     * String methodName = "myMethod";
+     * CharSequence[] paramTypeNames = new CharSequence[] { "java.lang.String", "int" };
+     * ExecutableElement method = MethodUtils.findMethod(typeElement, methodName, paramTypeNames);
+     *
+     * if (method != null) {
+     *     System.out.println("Found method: " + method.getSimpleName());
+     * } else {
+     *     System.out.println("Method not found.");
+     * }
+     * }
+ * + * @param type the type element to search for the method, may be null + * @param methodName the name of the method to find, must not be null + * @param parameterTypeNames the names of the parameter types of the method to match, must not be null + * @return the first matching executable element representing the method, or null if none is found + */ + @Nullable + static ExecutableElement findMethod(TypeElement type, String methodName, CharSequence... parameterTypeNames) { + return type == null ? null : findMethod(type.asType(), methodName, parameterTypeNames); + } + + /** + * Finds a method with the specified name and parameter type names in the given type mirror. + * + *

This method searches for a method with the specified name and exact parameter type names + * directly declared in the provided type mirror, excluding inherited methods. + * If no matching method is found, it returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * TypeMirror typeMirror = ...; // Obtain a valid TypeMirror
+     * String methodName = "myMethod";
+     * CharSequence[] paramTypeNames = new CharSequence[] { "java.lang.String", "int" };
+     * ExecutableElement method = MethodUtils.findMethod(typeMirror, methodName, paramTypeNames);
+     *
+     * if (method != null) {
+     *     System.out.println("Found method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param type the type mirror to search for the method, may be null + * @param methodName the name of the method to find, must not be null + * @param parameterTypeNames the names of the parameter types of the method to match, must not be null + * @return the first matching executable element representing the method, or null if none is found + */ + @Nullable + static ExecutableElement findMethod(TypeMirror type, String methodName, CharSequence... parameterTypeNames) { + if (type == null || methodName == null || parameterTypeNames == null) { + return null; + } + List allDeclaredMethods = findAllDeclaredMethods(type, method -> matches(method, methodName, parameterTypeNames)); + return allDeclaredMethods.isEmpty() ? null : allDeclaredMethods.get(0); + } + + /** + * Finds the overridden method in the specified type that corresponds to the given declaring method. + * + *

This method searches for a method in the provided type element that overrides the specified + * declaring method. It utilizes the processing environment to determine method overriding using + * the {@link Elements#overrides(ExecutableElement, ExecutableElement, TypeElement)} method. + * + *

Example Usage

+ *
{@code
+     * ProcessingEnvironment processingEnv = ...; // Obtain a valid ProcessingEnvironment
+     * TypeElement typeElement = ...; // The type in which to find the overridden method
+     * ExecutableElement declaringMethod = ...; // The method being overridden
+     *
+     * ExecutableElement overriddenMethod = getOverrideMethod(processingEnv, typeElement, declaringMethod);
+     *
+     * if (overriddenMethod != null) {
+     *     System.out.println("Overridden Method Found: " + overriddenMethod.getSimpleName());
+     * } else {
+     *     System.out.println("No overridden method found.");
+     * }
+     * }
+ * + * @param processingEnv the processing environment used to determine overriding + * @param type the type element in which to search for the overridden method + * @param declaringMethod the method element whose override is to be found + * @return the overridden method in the specified type, or null if no such method exists + */ + @Nullable + static ExecutableElement getOverrideMethod(ProcessingEnvironment processingEnv, TypeElement type, ExecutableElement declaringMethod) { + Elements elements = processingEnv.getElementUtils(); + return filterFirst(getAllDeclaredMethods(type), method -> elements.overrides(method, declaringMethod, type)); + } + + /** + * Filters the given list of executable elements (methods) based on the provided predicates. + * + *

This method applies a set of filtering predicates to a list of methods and returns a new list + * containing only the methods that satisfy all the provided conditions. If no method matches, + * an empty list is returned. If no filters are provided, the original list is returned unchanged. + * + *

Example Usage

+ *
{@code
+     * List methods = MethodUtils.getDeclaredMethods(typeElement);
+     * List publicNonStaticMethods = MethodUtils.filterMethods(methods, MethodUtils::isPublicNonStaticMethod);
+     *
+     * for (ExecutableElement method : publicNonStaticMethods) {
+     *     System.out.println("Public Non-Static Method: " + method.getSimpleName());
+     * }
+     * }
+ * + *

Multiple filters can be combined: + *

{@code
+     * Predicate isPublic = method -> method.getModifiers().contains(Modifier.PUBLIC);
+     * Predicate isVoidReturnType = method -> "void".equals(TypeUtils.toString(method.getReturnType()));
+     *
+     * List filteredMethods = MethodUtils.filterMethods(methods, isPublic, isVoidReturnType);
+     * for (ExecutableElement method : filteredMethods) {
+     *     System.out.println("Public Method with Void Return Type: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param methods the list of executable elements (methods) to be filtered + * @param methodFilters the array of predicates used for filtering the methods + * @return a filtered list of executable elements matching all the provided predicates, + * or an empty list if the input list is null or empty, or no methods match the filters + */ + @Nonnull + @Immutable + static List filterMethods(List methods, Predicate... methodFilters) { + if (isEmpty(methods)) { + return emptyList(); + } + + List filteredMethods = methods; + if (isNotEmpty(methodFilters)) { + Predicate combinedPredicate = and(methodFilters); + filteredMethods = methods.stream() + .filter(combinedPredicate) + .collect(toList()); + } + + return filteredMethods.isEmpty() ? emptyList() : unmodifiableList(filteredMethods); + } + + /** + * Gets the simple name of the method as a string. + * + *

If the provided method is null, this method returns null. + * Otherwise, it returns the simple name of the method as a string. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement representing a method
+     * String methodName = MethodUtils.getMethodName(method);
+     * if (methodName != null) {
+     *     System.out.println("Method Name: " + methodName);
+     * } else {
+     *     System.out.println("Method is null.");
+     * }
+     * }
+ * + * @param method the executable element representing the method, may be null + * @return the simple name of the method as a string, or null if the method is null + */ + @Nullable + static String getMethodName(ExecutableElement method) { + return method == null ? null : method.getSimpleName().toString(); + } + + /** + * Gets the return type name of the given method. + * + *

If the provided method is null, this method returns null. + * Otherwise, it returns the fully qualified name of the method's return type. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement representing a method
+     * String returnTypeName = MethodUtils.getReturnTypeName(method);
+     * if (returnTypeName != null) {
+     *     System.out.println("Return Type Name: " + returnTypeName);
+     * } else {
+     *     System.out.println("Method is null or has no return type.");
+     * }
+     * }
+ * + * @param method the executable element representing the method, may be null + * @return the fully qualified name of the method's return type as a string, or null if the method is null + */ + @Nullable + static String getReturnTypeName(ExecutableElement method) { + return method == null ? null : TypeUtils.toString(method.getReturnType()); + } + + /** + * Gets the parameter type mirrors of the given method. + * + *

This method returns a list of type mirrors representing the parameter types of the provided method. + * If the method is {@code null} or has no parameters, an empty list is returned. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement representing a method
+     * List parameterTypeMirrors = MethodUtils.getMethodParameterTypeMirrors(method);
+     *
+     * if (!parameterTypeMirrors.isEmpty()) {
+     *     for (TypeMirror typeMirror : parameterTypeMirrors) {
+     *         System.out.println("Parameter Type: " + TypeUtils.toString(typeMirror));
+     *     }
+     * } else {
+     *     System.out.println("Method is null or has no parameters.");
+     * }
+     * }
+ * + * @param method the executable element representing the method, may be null + * @return a list of type mirrors representing the parameter types of the method, + * or an empty list if the method is null or has no parameters + */ + @Nonnull + @Immutable + static List getMethodParameterTypeMirrors(ExecutableElement method) { + if (method == null) { + return emptyList(); + } + + List parameters = method.getParameters(); + if (parameters.isEmpty()) { + return emptyList(); + } + + List parameterTypes = parameters.stream() + .map(VariableElement::asType) + .collect(toList()); + + return unmodifiableList(parameterTypes); + } + + /** + * Gets the parameter type names of the given method. + * + *

This method returns an array of strings representing the fully qualified names + * of the parameter types of the provided method. If the method is {@code null} or has no parameters, + * an empty array is returned. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement representing a method
+     * String[] parameterTypeNames = MethodUtils.getMethodParameterTypeNames(method);
+     *
+     * if (parameterTypeNames.length > 0) {
+     *     for (String typeName : parameterTypeNames) {
+     *         System.out.println("Parameter Type: " + typeName);
+     *     }
+     * } else {
+     *     System.out.println("Method is null or has no parameters.");
+     * }
+     * }
+ * + * @param method the executable element representing the method, may be null + * @return an array of strings representing the fully qualified names of the parameter types, + * or an empty array if the method is null or has no parameters + */ + static String[] getMethodParameterTypeNames(ExecutableElement method) { + List parameterTypes = getMethodParameterTypeMirrors(method); + return parameterTypes.isEmpty() ? EMPTY_STRING_ARRAY : + parameterTypes.stream().map(TypeUtils::toString).toArray(String[]::new); + } + + /** + * Checks if the given method matches the specified method name and parameter types. + * + *

This method determines whether the provided executable element (method) has the same name + * and parameter types as specified. If the method is null or any of the parameters are null, + * the result will be false. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
+     * boolean isMatch = matches(method, "myMethod", String.class, int.class);
+     * if (isMatch) {
+     *     System.out.println("The method matches the specified name and parameter types.");
+     * } else {
+     *     System.out.println("The method does not match.");
+     * }
+     * }
+ * + * @param method the executable element representing the method to check + * @param methodName the name of the method to match + * @param parameterTypes the parameter types of the method to match + * @return true if the method matches the given name and parameter types, false otherwise + */ + static boolean matches(ExecutableElement method, String methodName, Type... parameterTypes) { + return matchesMethod(method, methodName, parameterTypes); + } + + /** + * Checks if the given method matches the specified method name and parameter type names. + * + *

This method determines whether the provided executable element (method) has the same name + * and parameter type names as specified. If the method is null or any of the parameters are null, + * the result will be false. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
+     * boolean isMatch = matches(method, "myMethod", "java.lang.String", "int");
+     * if (isMatch) {
+     *     System.out.println("The method matches the specified name and parameter type names.");
+     * } else {
+     *     System.out.println("The method does not match.");
+     * }
+     * }
+ * + * @param method the executable element representing the method to check + * @param methodName the name of the method to match + * @param parameterTypeNames the names of the parameter types to match + * @return true if the method matches the given name and parameter type names, false otherwise + */ + static boolean matches(ExecutableElement method, String methodName, CharSequence... parameterTypeNames) { + return matchesMethod(method, methodName, parameterTypeNames); + } + + /** + * Checks if the given method has the specified method name. + * + *

This method compares the simple name of the provided executable element (method) + * with the given method name. If either the method or the method name is {@code null}, + * the comparison is performed using {@link Objects#equals(Object, Object)}. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
+     * boolean isMatch = MethodUtils.matchesMethodName(method, "myMethod");
+     *
+     * if (isMatch) {
+     *     System.out.println("The method name matches.");
+     * } else {
+     *     System.out.println("The method name does not match.");
+     * }
+     * }
+ * + * @param method the executable element representing the method to check + * @param methodName the name of the method to match, may be null + * @return true if the method's name matches the given method name, false otherwise + */ + static boolean matchesMethodName(ExecutableElement method, String methodName) { + return Objects.equals(getMethodName(method), methodName); + } + + /** + * Checks if the given method matches the specified method name and parameter types. + * + *

This method determines whether the provided executable element (method) has the same name + * and parameter types as specified. If the method is null or any of the parameters are null, + * the result will be false. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
+     * boolean isMatch = MethodUtils.matchesMethod(method, "myMethod", String.class, int.class);
+     * if (isMatch) {
+     *     System.out.println("The method matches the specified name and parameter types.");
+     * } else {
+     *     System.out.println("The method does not match.");
+     * }
+     * }
+ * + * @param method the executable element representing the method to check + * @param methodName the name of the method to match + * @param parameterTypes the parameter types of the method to match + * @return true if the method matches the given name and parameter types, false otherwise + */ + static boolean matchesMethod(ExecutableElement method, String methodName, Type... parameterTypes) { + if (method == null || methodName == null || parameterTypes == null) { + return false; + } + + // Check if the method name matches + if (!matchesMethodName(method, methodName)) { + return false; + } + + // Check if the parameter types match + if (!matchParameterTypes(method, parameterTypes)) { + return false; + } + + return true; + } + + /** + * Checks if the given method matches the specified method name and parameter type names. + * + *

This method determines whether the provided executable element (method) has the same name + * and parameter type names as specified. If the method is null or any of the parameters are null, + * the result will be false. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
+     * boolean isMatch = MethodUtils.matchesMethod(method, "myMethod", "java.lang.String", "int");
+     * if (isMatch) {
+     *     System.out.println("The method matches the specified name and parameter type names.");
+     * } else {
+     *     System.out.println("The method does not match.");
+     * }
+     * }
+ * + * @param method the executable element representing the method to check + * @param methodName the name of the method to match + * @param parameterTypeNames the names of the parameter types to match + * @return true if the method matches the given name and parameter type names, false otherwise + */ + static boolean matchesMethod(ExecutableElement method, String methodName, CharSequence... parameterTypeNames) { + if (method == null || methodName == null || parameterTypeNames == null) { + return false; + } + + // matches the name of method + if (!Objects.equals(getMethodName(method), methodName)) { + return false; + } + + if (!matchParameterTypeNames(method.getParameters(), parameterTypeNames)) { + return false; + } + + return true; + } + + /** + * Returns the enclosing element of the given executable method. + * + *

This method retrieves the element that directly encloses the provided method. + * If the method is {@code null}, the method returns {@code null}. + * + *

Example Usage

+ *
{@code
+     * ExecutableElement method = ...; // Obtain a valid ExecutableElement
+     * Element enclosingElement = MethodUtils.getEnclosingElement(method);
+     * if (enclosingElement != null) {
+     *     System.out.println("Enclosing Element: " + enclosingElement.getSimpleName());
+     * } else {
+     *     System.out.println("Method is null or has no enclosing element.");
+     * }
+     * }
+ * + * @param method the executable element representing the method, may be null + * @return the enclosing element of the method, or null if the method is null + */ + @Nullable + static Element getEnclosingElement(ExecutableElement method) { + return method == null ? null : method.getEnclosingElement(); + } + + /** + * Creates a predicate that filters out methods declared in the specified excluded types. + * + *

This predicate can be used to exclude methods that are declared in certain types, + * such as standard Java types like {@link Object}, when searching or filtering through methods. + * If no excluded types are provided, the predicate will allow all methods. + * + *

Example Usage

+ *
{@code
+     * Predicate excludeObjectMethods = methodPredicateForExcludedTypes(Object.class);
+     * List filteredMethods = findDeclaredMethods(typeMirror, excludeObjectMethods);
+     *
+     * for (ExecutableElement method : filteredMethods) {
+     *     System.out.println("Filtered Method: " + method.getSimpleName());
+     * }
+     * }
+ * + * @param excludedTypes the types whose methods should be excluded from the result, optional + * @return a predicate that returns {@code true} for methods not declared in any of the excluded types + */ + @Nonnull + static Predicate methodPredicateForExcludedTypes(Type... excludedTypes) { + return method -> { + boolean excluded = true; + Element declaredType = getEnclosingElement(method); + for (Type excludedType : excludedTypes) { + if (isSameType(declaredType, excludedType)) { + excluded = false; + break; + } + } + return excluded; + }; + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java new file mode 100644 index 000000000..7a4653fe8 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java @@ -0,0 +1,200 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.AnnotationValueVisitor; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleAnnotationValueVisitor6; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import static io.microsphere.lang.model.util.AnnotationUtils.getAttributeName; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValues; +import static io.microsphere.lang.model.util.ClassUtils.loadClass; +import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; +import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; +import static io.microsphere.util.ArrayUtils.newArray; +import static io.microsphere.util.ClassLoaderUtils.resolveClass; +import static java.lang.Enum.valueOf; +import static java.lang.reflect.Array.set; + +/** + * A visitor for resolving annotation values into their corresponding runtime representations. + * + *

This class extends {@link SimpleAnnotationValueVisitor6} to process annotation values and convert + * them into appropriate Java objects such as primitives, strings, enums, classes, annotations, and arrays. + * + *

Example Usage

+ *
{@code
+ * // Create an instance with default settings
+ * ResolvableAnnotationValueVisitor visitor = new ResolvableAnnotationValueVisitor();
+ *
+ * // Visit an annotation value
+ * AnnotationValue annotationValue = ...; // Obtain from an annotation mirror
+ * Object resolvedValue = annotationValue.accept(visitor, executableElement);
+ * }
+ * + *

Custom behavior examples

+ *
{@code
+ * // Create an instance that represents Class values as strings
+ * ResolvableAnnotationValueVisitor visitor = new ResolvableAnnotationValueVisitor(true);
+ *
+ * // Create an instance that handles nested annotations as maps
+ * ResolvableAnnotationValueVisitor visitor = new ResolvableAnnotationValueVisitor(false, true);
+ * }
+ * + * @author
Mercy + * @see AnnotationValueVisitor + * @since 1.0.0 + */ +public class ResolvableAnnotationValueVisitor extends SimpleAnnotationValueVisitor6 { + + private static final Class ANNOTATION_PARSER_CLASS = resolveClass("sun.reflect.annotation.AnnotationParser"); + + private static final Method annotationForMapMethod = findMethod(ANNOTATION_PARSER_CLASS, "annotationForMap", Class.class, Map.class); + + private static final boolean DEFAULT_CLASS_VALUES_AS_STRING = false; + + private static final boolean DEFAULT_NESTED_ANNOTATIONS_AS_MAP = false; + + private final boolean classValuesAsString; + + private final boolean nestedAnnotationsAsMap; + + public ResolvableAnnotationValueVisitor() { + this(DEFAULT_CLASS_VALUES_AS_STRING, DEFAULT_NESTED_ANNOTATIONS_AS_MAP); + } + + public ResolvableAnnotationValueVisitor(boolean classValuesAsString) { + this(classValuesAsString, DEFAULT_NESTED_ANNOTATIONS_AS_MAP); + } + + public ResolvableAnnotationValueVisitor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + this.classValuesAsString = classValuesAsString; + this.nestedAnnotationsAsMap = nestedAnnotationsAsMap; + } + + @Override + public Object visitBoolean(boolean b, ExecutableElement attributeMethod) { + return b; + } + + @Override + public Object visitByte(byte b, ExecutableElement attributeMethod) { + return b; + } + + @Override + public Object visitChar(char c, ExecutableElement attributeMethod) { + return c; + } + + @Override + public Object visitDouble(double d, ExecutableElement attributeMethod) { + return d; + } + + @Override + public Object visitFloat(float f, ExecutableElement attributeMethod) { + return f; + } + + @Override + public Object visitInt(int i, ExecutableElement attributeMethod) { + return i; + } + + @Override + public Object visitLong(long i, ExecutableElement attributeMethod) { + return i; + } + + @Override + public Object visitShort(short s, ExecutableElement attributeMethod) { + return s; + } + + @Override + public Object visitString(String s, ExecutableElement attributeMethod) { + return s; + } + + @Override + public Object visitType(TypeMirror t, ExecutableElement attributeMethod) { + return classValuesAsString ? t.toString() : loadClass(t); + } + + @Override + public Object visitEnumConstant(VariableElement c, ExecutableElement attributeMethod) { + Class enumClass = loadClass(c.asType()); + return valueOf(enumClass, c.toString()); + } + + @Override + public Object visitAnnotation(AnnotationMirror a, ExecutableElement attributeMethod) { + Map elementValues = getElementValues(a); + Map attributesMap = newFixedLinkedHashMap(elementValues.size()); + for (Entry entry : elementValues.entrySet()) { + ExecutableElement method = entry.getKey(); + String attributeName = getAttributeName(method); + Object attributeValue = entry.getValue().accept(this, method); + attributesMap.put(attributeName, attributeValue); + } + + if (nestedAnnotationsAsMap) { + return attributesMap; + } + + Class annotationClass = loadClass(a.getAnnotationType()); + return invokeStaticMethod(annotationForMapMethod, annotationClass, attributesMap); + } + + @Override + public Object visitArray(List values, ExecutableElement attributeMethod) { + int size = values.size(); + Class componentType = getComponentType(attributeMethod); + Object array = newArray(componentType, size); + for (int i = 0; i < size; i++) { + AnnotationValue value = values.get(i); + Object attributeValue = value.accept(this, attributeMethod); + set(array, i, attributeValue); + } + return array; + } + + Class getComponentType(ExecutableElement attributeMethod) { + if (classValuesAsString) { + return String.class; + } + ArrayType arrayType = (ArrayType) attributeMethod.getReturnType(); + return loadClass(arrayType.getComponentType()); + } + + @Override + public Object visitUnknown(AnnotationValue av, ExecutableElement attributeMethod) { + return av; + } +} diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java new file mode 100644 index 000000000..06692f441 --- /dev/null +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java @@ -0,0 +1,2505 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.annotation.Immutable; +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.util.TypeFinder; +import io.microsphere.util.Utils; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.constants.SymbolConstants.GREATER_THAN_CHAR; +import static io.microsphere.constants.SymbolConstants.LESS_THAN_CHAR; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static io.microsphere.lang.function.Predicates.and; +import static io.microsphere.lang.function.Streams.filterFirst; +import static io.microsphere.reflect.MethodUtils.invokeMethod; +import static io.microsphere.reflect.TypeUtils.getTypeNames; +import static io.microsphere.util.ArrayUtils.contains; +import static io.microsphere.util.ArrayUtils.isEmpty; +import static io.microsphere.util.Assert.assertNoNullElements; +import static io.microsphere.util.ClassUtils.SIMPLE_TYPES; +import static java.lang.String.valueOf; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Stream.of; +import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE; +import static javax.lang.model.element.ElementKind.CLASS; +import static javax.lang.model.element.ElementKind.ENUM; +import static javax.lang.model.element.ElementKind.INTERFACE; +import static javax.lang.model.type.TypeKind.ARRAY; + +/** + * The utilities class for type in the package "javax.lang.model.*" + * + * @author Mercy + * @since 1.0.0 + */ +public interface TypeUtils extends Utils { + + /** + * A list of names representing simple types in Java. + * Simple types include primitive types, void, and commonly used basic classes like String, Number, etc. + */ + @Nonnull + @Immutable + List SIMPLE_TYPE_NAMES = ofList( + SIMPLE_TYPES + .stream() + .map(Class::getName) + .toArray(String[]::new) + ); + + /** + * Get the superclass of the specified type + */ + @Nonnull + Function TYPE_ELEMENT_GET_SUPERCLASS = type -> ofTypeElement(type.getSuperclass()); + + /** + * Get the interfaces of the specified type + */ + @Nonnull + Function TYPE_ELEMENT_GET_INTERFACES = type -> type.getInterfaces() + .stream() + .map(TypeUtils::ofTypeElement) + .toArray(TypeElement[]::new); + + + /** + * Checks if the given Element represents a simple type. + * A simple type is defined as a basic data type that can be directly represented without further resolution. + * + *

+ * Examples of simple types include primitive types (e.g., int, boolean), + * built-in types like void, and commonly used basic classes like String or Number. + *

+ * + *

Example Usage

+ *
{@code
+     * Element intElement = ...; // represents 'int'
+     * boolean isSimple = TypeUtils.isSimpleType(intElement); // returns true
+     *
+     * Element customClassElement = ...; // represents a custom class like 'MyClass'
+     * boolean isSimple = TypeUtils.isSimpleType(customClassElement); // returns false
+     * }
+ * + * @param element the element to check, may be null + * @return true if the element is a simple type, false otherwise + */ + static boolean isSimpleType(Element element) { + return element != null && isSimpleType(element.asType()); + } + + /** + * Checks whether the given {@link TypeMirror} represents a simple type. + * A simple type is one that can be directly represented without further resolution, + * such as primitive types, built-in types like void, or commonly recognized basic types. + * + *

+ * Examples of simple types include: + *

    + *
  • Primitive types: int, boolean, char, etc.
  • + *
  • Built-in types: void, java.lang.String, java.lang.Number
  • + *
+ *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror intType = ...; // represents 'int'
+     * boolean isSimple = TypeUtils.isSimpleType(intType); // returns true
+     *
+     * TypeMirror customType = ...; // represents a custom class like 'MyClass'
+     * boolean isSimple = TypeUtils.isSimpleType(customType); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the type is a simple type; false otherwise + */ + static boolean isSimpleType(TypeMirror type) { + return type != null && SIMPLE_TYPE_NAMES.contains(type.toString()); + } + + /** + * Checks if the given Element and Type represent the same type. + * + *

+ * This method compares the type information of the provided Element and Type objects + * to determine if they represent the same type. If either parameter is null, the comparison + * is made based on whether both are null. + *

+ * + *

Example Usage

+ *
{@code
+     * Element element = ...; // represents a type like String
+     * Type type = ...; // represents the same type as the element
+     * boolean isSame = TypeUtils.isSameType(element, type); // returns true
+     *
+     * Element element2 = ...; // represents a different type
+     * Type type2 = ...; // represents a different type
+     * boolean isSame2 = TypeUtils.isSameType(element2, type2); // returns false
+     * }
+ * + * @param element the Element to compare, may be null + * @param type the Type to compare, may be null + * @return true if both represent the same type; false otherwise + */ + static boolean isSameType(Element element, Type type) { + return isSameType(element == null ? null : element.asType(), type); + } + + /** + * Checks if the given Element and type name represent the same type. + * + *

+ * This method compares the type information of the provided Element and the fully qualified class name + * to determine if they represent the same type. If either parameter is null, the comparison + * is made based on whether both are null. + *

+ * + *

Example Usage

+ *
{@code
+     * Element stringElement = ...; // represents 'java.lang.String'
+     * boolean isSame = TypeUtils.isSameType(stringElement, "java.lang.String"); // returns true
+     *
+     * Element customElement = ...; // represents 'com.example.MyClass'
+     * boolean isSame2 = TypeUtils.isSameType(customElement, "com.example.OtherClass"); // returns false
+     * }
+ * + * @param type the Element to compare, may be null + * @param typeName the fully qualified class name to compare, may be null + * @return true if both represent the same type; false otherwise + */ + static boolean isSameType(Element type, CharSequence typeName) { + return isSameType(type == null ? null : type.asType(), typeName); + } + + /** + * Checks if the given {@link TypeMirror} and {@link Type} represent the same type. + * + *

+ * This method compares the fully qualified type name of the {@link TypeMirror} with the + * {@link Type#getTypeName() type name} of the provided {@link Type} to determine if they + * represent the same type. If both are {@code null}, they are considered the same. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror stringTypeMirror = element.asType(); // represents 'java.lang.String'
+     * Type stringType = Class.forName("java.lang.String");
+     * boolean isSame = TypeUtils.isSameType(stringTypeMirror, stringType); // returns true
+     *
+     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
+     * Type intType = int.class;
+     * boolean isSamePrimitive = TypeUtils.isSameType(intTypeMirror, intType); // returns true
+     *
+     * TypeMirror customTypeMirror = ...; // represents 'com.example.MyClass'
+     * Type customType = Class.forName("com.example.OtherClass");
+     * boolean isSameCustom = TypeUtils.isSameType(customTypeMirror, customType); // returns false
+     * }
+ * + * @param typeMirror the TypeMirror to compare, may be null + * @param type the Type to compare, may be null + * @return true if both represent the same type; false otherwise + */ + static boolean isSameType(TypeMirror typeMirror, Type type) { + return isSameType(typeMirror, type == null ? null : type.getTypeName()); + } + + /** + * Checks if the given TypeMirror and type name represent the same type. + * + *

+ * This method compares the fully qualified type name of the TypeMirror with the provided + * typeName to determine if they represent the same type. If both are null, they are considered + * the same. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror stringTypeMirror = element.asType(); // represents 'java.lang.String'
+     * boolean isSame = TypeUtils.isSameType(stringTypeMirror, "java.lang.String"); // returns true
+     *
+     * TypeMirror customTypeMirror = ...; // represents 'com.example.MyClass'
+     * boolean isSameCustom = TypeUtils.isSameType(customTypeMirror, "com.example.OtherClass"); // returns false
+     * }
+ * + * @param type the TypeMirror to compare, may be null + * @param typeName the fully qualified class name to compare, may be null + * @return true if both represent the same type; false otherwise + */ + static boolean isSameType(TypeMirror type, CharSequence typeName) { + if (type == null && typeName == null) { + return true; + } + return Objects.equals(valueOf(type), valueOf(typeName)); + } + + /** + * Checks whether the given {@link TypeMirror} represents an array type. + * + *

+ * This method determines if the provided TypeMirror corresponds to an array type + * by checking its kind against the array type kind defined in the Java language model. + * If the provided TypeMirror is null, the method returns false. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror arrayTypeMirror = processingEnv.getTypeUtils().getArrayType(...); // represents an array type
+     * boolean isArray = TypeUtils.isArrayType(arrayTypeMirror); // returns true
+     *
+     * TypeMirror stringTypeMirror = element.asType(); // represents 'java.lang.String'
+     * boolean isArray = TypeUtils.isArrayType(stringTypeMirror); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the type is an array type; false otherwise + */ + static boolean isArrayType(TypeMirror type) { + return type != null && ARRAY == type.getKind(); + } + + /** + * Checks whether the given Element represents an array type. + * + *

+ * This method determines if the provided Element corresponds to an array type + * by checking its kind against the array type kind defined in the Java language model. + * If the provided Element is null, the method returns false. + *

+ * + *

Example Usage

+ *
{@code
+     * Element arrayElement = ...; // represents an array type like String[]
+     * boolean isArray = TypeUtils.isArrayType(arrayElement); // returns true
+     *
+     * Element stringElement = ...; // represents 'java.lang.String'
+     * boolean isArray = TypeUtils.isArrayType(stringElement); // returns false
+     * }
+ * + * @param element the Element to check, may be null + * @return true if the element represents an array type; false otherwise + */ + static boolean isArrayType(Element element) { + return element != null && isArrayType(element.asType()); + } + + /** + * Checks whether the given {@link TypeMirror} represents an enum type. + * + *

+ * This method determines if the provided TypeMirror corresponds to an enum type + * by checking its kind via the underlying element's {@link Element#getKind()}. + * If the provided TypeMirror is null or cannot be resolved to a declared type, + * the method returns false. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror enumTypeMirror = element.asType(); // represents an enum type like MyEnum
+     * boolean isEnum = TypeUtils.isEnumType(enumTypeMirror); // returns true
+     *
+     * TypeMirror stringTypeMirror = element.asType(); // represents 'java.lang.String'
+     * boolean isEnum = TypeUtils.isEnumType(stringTypeMirror); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the type is an enum type; false otherwise + */ + static boolean isEnumType(TypeMirror type) { + DeclaredType declaredType = ofDeclaredType(type); + return declaredType != null && ENUM == declaredType.asElement().getKind(); + } + + /** + * Checks whether the given Element represents an enum type. + * + *

+ * This method determines if the provided Element corresponds to an enum type + * by checking its kind via the underlying element's {@link Element#getKind()}. + * If the provided Element is null or cannot be resolved to a declared type, + * the method returns false. + *

+ * + *

Example Usage

+ *
{@code
+     * Element enumElement = ...; // represents an enum type like MyEnum
+     * boolean isEnum = TypeUtils.isEnumType(enumElement); // returns true
+     *
+     * Element stringElement = ...; // represents 'java.lang.String'
+     * boolean isEnum = TypeUtils.isEnumType(stringElement); // returns false
+     * }
+ * + * @param element the Element to check, may be null + * @return true if the element represents an enum type; false otherwise + */ + static boolean isEnumType(Element element) { + return element != null && isEnumType(element.asType()); + } + + /** + * Checks whether the given {@link TypeMirror} represents a class type. + * A class type is determined by checking if its corresponding element has the kind of a class. + * + *

Example Usage

+ *
{@code
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * boolean isClass = TypeUtils.isClassType(classTypeMirror); // returns true
+     *
+     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
+     * boolean isClass = TypeUtils.isClassType(interfaceTypeMirror); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the type is a class type; false otherwise + */ + static boolean isClassType(TypeMirror type) { + DeclaredType declaredType = ofDeclaredType(type); + return declaredType != null && isClassType(declaredType.asElement()); + } + + /** + * Checks whether the given Element represents a class type. + * A class type is determined by checking if its corresponding element has the kind of a class. + * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * boolean isClass = TypeUtils.isClassType(classElement); // returns true
+     *
+     * Element interfaceElement = ...; // represents an interface like MyInterface
+     * boolean isClass = TypeUtils.isClassType(interfaceElement); // returns false
+     * }
+ * + * @param element the Element to check, may be null + * @return true if the element represents a class type; false otherwise + */ + static boolean isClassType(Element element) { + return element != null && CLASS == element.getKind(); + } + + /** + * Checks whether the given {@link TypeMirror} represents a primitive type. + * A primitive type is one of the predefined types in Java such as int, boolean, etc. + * + *

Example Usage

+ *
{@code
+     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
+     * boolean isPrimitive = TypeUtils.isPrimitiveType(intTypeMirror); // returns true
+     *
+     * TypeMirror stringTypeMirror = elementUtils.getTypeElement("java.lang.String").asType();
+     * boolean isPrimitiveString = TypeUtils.isPrimitiveType(stringTypeMirror); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the type is a primitive type; false otherwise + */ + static boolean isPrimitiveType(TypeMirror type) { + return type != null && type.getKind().isPrimitive(); + } + + /** + * Checks whether the given Element represents a primitive type. + * A primitive type is one of the predefined types in Java such as int, boolean, etc. + * + *

Example Usage

+ *
{@code
+     * Element intElement = ...; // represents 'int'
+     * boolean isPrimitive = TypeUtils.isPrimitiveType(intElement); // returns true
+     *
+     * Element stringElement = ...; // represents 'java.lang.String'
+     * boolean isPrimitiveString = TypeUtils.isPrimitiveType(stringElement); // returns false
+     * }
+ * + * @param element the Element to check, may be null + * @return true if the element represents a primitive type; false otherwise + */ + static boolean isPrimitiveType(Element element) { + return element != null && isPrimitiveType(element.asType()); + } + + /** + * Checks whether the given {@link TypeMirror} represents an interface type. + * An interface type is determined by checking if its corresponding element has the kind of an interface. + * + *

Example Usage

+ *
{@code
+     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
+     * boolean isInterface = TypeUtils.isInterfaceType(interfaceTypeMirror); // returns true
+     *
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * boolean isInterface = TypeUtils.isInterfaceType(classTypeMirror); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the type is an interface type; false otherwise + */ + static boolean isInterfaceType(TypeMirror type) { + DeclaredType declaredType = ofDeclaredType(type); + return declaredType != null && isInterfaceType(declaredType.asElement()); + } + + /** + * Checks whether the given Element represents an interface type. + * An interface type is determined by checking if its corresponding element has the kind of an interface. + * + *

Example Usage

+ *
{@code
+     * Element interfaceElement = ...; // represents an interface like MyInterface
+     * boolean isInterface = TypeUtils.isInterfaceType(interfaceElement); // returns true
+     *
+     * Element classElement = ...; // represents a class like MyClass
+     * boolean isInterface = TypeUtils.isInterfaceType(classElement); // returns false
+     * }
+ * + * @param element the Element to check, may be null + * @return true if the element represents an interface type; false otherwise + */ + static boolean isInterfaceType(Element element) { + return element != null && INTERFACE == element.getKind(); + } + + /** + * Checks whether the given {@link TypeMirror} represents an annotation type. + * An annotation type is determined by checking if its corresponding element has the kind of an annotation. + * + *

Example Usage

+ *
{@code
+     * TypeMirror annotationTypeMirror = element.asType(); // represents an annotation like MyAnnotation
+     * boolean isAnnotation = TypeUtils.isAnnotationType(annotationTypeMirror); // returns true
+     *
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * boolean isAnnotation = TypeUtils.isAnnotationType(classTypeMirror); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the type is an annotation type; false otherwise + */ + static boolean isAnnotationType(TypeMirror type) { + DeclaredType declaredType = ofDeclaredType(type); + return declaredType != null && isAnnotationType(declaredType.asElement()); + } + + /** + * Checks whether the given Element represents an annotation type. + * An annotation type is determined by checking if its corresponding element has the kind of an annotation. + * + *

Example Usage

+ *
{@code
+     * Element annotationElement = ...; // represents an annotation like MyAnnotation
+     * boolean isAnnotation = TypeUtils.isAnnotationType(annotationElement); // returns true
+     *
+     * Element classElement = ...; // represents a class like MyClass
+     * boolean isAnnotation = TypeUtils.isAnnotationType(classElement); // returns false
+     * }
+ * + * @param element the Element to check, may be null + * @return true if the element represents an annotation type; false otherwise + */ + static boolean isAnnotationType(Element element) { + return element != null && ANNOTATION_TYPE == element.getKind(); + } + + /** + * Checks if the given Element is a TypeElement. + * + *

+ * This method verifies whether the provided Element represents a type element, + * such as a class, interface, enum, or annotation type. If the element is null, + * it returns false. + *

+ * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * boolean isType = TypeUtils.isTypeElement(classElement); // returns true
+     *
+     * Element packageElement = ...; // represents a package
+     * boolean isType = TypeUtils.isTypeElement(packageElement); // returns false
+     * }
+ * + * @param element The Element to check, may be null. + * @return true if the element is a TypeElement; false otherwise. + */ + static boolean isTypeElement(Element element) { + return element instanceof TypeElement; + } + + /** + * Checks if the given TypeMirror represents a TypeElement. + * A TypeElement is a type that corresponds to a class, interface, enum, or annotation type + * that has been explicitly defined in the source code. + * + *

Example Usage

+ *
{@code
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * boolean isType = TypeUtils.isTypeElement(classTypeMirror); // returns true
+     *
+     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
+     * boolean isTypePrimitive = TypeUtils.isTypeElement(intTypeMirror); // returns false
+     *
+     * TypeMirror stringArrayTypeMirror = processingEnv.getTypeUtils().getArrayType(...); // represents String[]
+     * boolean isTypeArray = TypeUtils.isTypeElement(stringArrayTypeMirror); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the TypeMirror represents a TypeElement; false otherwise + */ + static boolean isTypeElement(TypeMirror type) { + DeclaredType declaredType = ofDeclaredType(type); + return declaredType != null && isTypeElement(declaredType.asElement()); + } + + /** + * Checks whether the given Element represents a declared type. + * A declared type is a type that corresponds to a class, interface, enum, or annotation type + * that has been explicitly defined in the source code. + * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * boolean isDeclared = TypeUtils.isDeclaredType(classElement); // returns true
+     *
+     * Element packageElement = ...; // represents a package
+     * boolean isDeclaredPackage = TypeUtils.isDeclaredType(packageElement); // returns false
+     * }
+ * + * @param element the Element to check, may be null + * @return true if the element represents a DeclaredType; false otherwise + */ + static boolean isDeclaredType(Element element) { + return element != null && isDeclaredType(element.asType()); + } + + /** + * Checks whether the given {@link TypeMirror} represents a declared type. + * A declared type is a type that corresponds to a class, interface, enum, or annotation type + * that has been explicitly defined in the source code. + * + *

Example Usage

+ *
{@code
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * boolean isDeclared = TypeUtils.isDeclaredType(classTypeMirror); // returns true
+     *
+     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
+     * boolean isDeclaredPrimitive = TypeUtils.isDeclaredType(intTypeMirror); // returns false
+     *
+     * TypeMirror stringArrayTypeMirror = processingEnv.getTypeUtils().getArrayType(...); // represents String[]
+     * boolean isDeclaredArray = TypeUtils.isDeclaredType(stringArrayTypeMirror); // returns false
+     * }
+ * + * @param type the TypeMirror to check, may be null + * @return true if the type is a declared type; false otherwise + */ + static boolean isDeclaredType(TypeMirror type) { + return type instanceof DeclaredType; + } + + /** + * Converts the given Element to a TypeElement if it is an instance of TypeElement. + * + *

+ * This method checks if the provided Element represents a type element such as a class, interface, + * enum, or annotation type. If the element is null or not a TypeElement, this method returns null. + *

+ * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * TypeElement typeElement = TypeUtils.ofTypeElement(classElement); // returns a valid TypeElement
+     *
+     * Element packageElement = ...; // represents a package
+     * TypeElement typeElementForPackage = TypeUtils.ofTypeElement(packageElement); // returns null
+     * }
+ * + * @param element The Element to convert, may be null. + * @return The converted TypeElement if the element is a TypeElement; otherwise, null. + */ + static TypeElement ofTypeElement(Element element) { + return isTypeElement(element) ? (TypeElement) element : null; + } + + /** + * Converts the given TypeMirror to a TypeElement if it represents a declared type. + * + *

+ * This method checks if the provided TypeMirror corresponds to a declared type + * (such as a class, interface, enum, or annotation type). If it does, the corresponding + * TypeElement is returned. If the TypeMirror is null or not a declared type, + * this method returns null. + *

+ * + *

Example Usage

+ *
{@code
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * TypeElement typeElement = TypeUtils.ofTypeElement(classTypeMirror); // returns a valid TypeElement
+     *
+     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
+     * TypeElement primitiveTypeElement = TypeUtils.ofTypeElement(intTypeMirror); // returns null
+     * }
+ * + * @param type The TypeMirror to convert, may be null. + * @return The corresponding TypeElement if the TypeMirror represents a declared type; + * otherwise, null if the type is null or not a DeclaredType. + */ + @Nullable + static TypeElement ofTypeElement(TypeMirror type) { + DeclaredType declaredType = ofDeclaredType(type); + return ofTypeElement(declaredType); + } + + /** + * Converts the given DeclaredType to a TypeElement if it is not null. + * + *

This method attempts to convert the provided {@link DeclaredType} to a corresponding + * {@link TypeElement}. If the declared type is null, or if it cannot be resolved to a type element, + * this method returns null. + * + *

Example Usage

+ *
{@code
+     * DeclaredType declaredType = ...; // represents a declared type like MyClass
+     * TypeElement typeElement = TypeUtils.ofTypeElement(declaredType); // returns a valid TypeElement if available
+     *
+     * DeclaredType nullDeclaredType = null;
+     * TypeElement nullTypeElement = TypeUtils.ofTypeElement(nullDeclaredType); // returns null
+     * }
+ * + * @param declaredType the DeclaredType to convert, may be null + * @return the corresponding TypeElement if the DeclaredType is not null; + * otherwise, null + */ + @Nullable + static TypeElement ofTypeElement(DeclaredType declaredType) { + if (declaredType != null) { + return ofTypeElement(declaredType.asElement()); + } + return null; + } + + /** + * Converts the given Element to a DeclaredType by first converting it to a TypeElement. + * If the element is null or cannot be converted to a DeclaredType, returns null. + * + *

+ * This method checks if the provided Element represents a type element (class, interface, enum, or annotation). + * If it does, the corresponding TypeElement is obtained, and its asType() method is called to retrieve the TypeMirror. + * Then, the TypeMirror is converted to a DeclaredType if it represents a declared type. + *

+ * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * DeclaredType declaredType = TypeUtils.ofDeclaredType(classElement); // returns a valid DeclaredType
+     *
+     * Element packageElement = ...; // represents a package
+     * DeclaredType packageDeclaredType = TypeUtils.ofDeclaredType(packageElement); // returns null
+     * }
+ * + * @param element The Element to convert, may be null. + * @return The corresponding DeclaredType if the element is valid and represents a declared type; + * otherwise, null if the element is null or conversion fails. + */ + @Nullable + static DeclaredType ofDeclaredType(Element element) { + return element == null ? null : ofDeclaredType(element.asType()); + } + + /** + * Converts the given TypeMirror to a DeclaredType if it represents a declared type. + * A declared type is a type that corresponds to a class, interface, enum, or annotation type + * that has been explicitly defined in the source code. + * + *

Example Usage

+ *
{@code
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * DeclaredType declaredType = TypeUtils.ofDeclaredType(classTypeMirror); // returns a valid DeclaredType
+     *
+     * TypeMirror intTypeMirror = processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT);
+     * DeclaredType primitiveDeclaredType = TypeUtils.ofDeclaredType(intTypeMirror); // returns null
+     *
+     * TypeMirror stringArrayTypeMirror = processingEnv.getTypeUtils().getArrayType(...); // represents String[]
+     * DeclaredType arrayDeclaredType = TypeUtils.ofDeclaredType(stringArrayTypeMirror); // returns null
+     * }
+ * + * @param type The TypeMirror to convert, may be null. + * @return The corresponding DeclaredType if the TypeMirror represents a declared type; + * otherwise, null if the type is null or not a DeclaredType. + */ + @Nullable + static DeclaredType ofDeclaredType(TypeMirror type) { + return isDeclaredType(type) ? (DeclaredType) type : null; + } + + /** + * Converts an array of Elements to a List of TypeMirrors. + * If the input array is null or empty, returns an empty list. + * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * Element interfaceElement = ...; // represents an interface like MyInterface
+     *
+     * List typeMirrors = TypeUtils.ofTypeMirrors(classElement, interfaceElement);
+     * // typeMirrors now contains the TypeMirror of MyClass and MyInterface
+     *
+     * List emptyList = TypeUtils.ofTypeMirrors(); // returns an empty list
+     * }
+ * + * @param elements the array of Elements to convert + * @return a List of TypeMirrors derived from the given Elements + */ + @Nonnull + @Immutable + static List ofTypeMirrors(Element... elements) { + return ofTypeMirrors(ofList(elements)); + } + + /** + * Converts a collection of Elements to a list of TypeMirrors. + * Optionally applies an array of predicates to filter the resulting TypeMirrors. + * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * Element interfaceElement = ...; // represents an interface like MyInterface
+     *
+     * List typeMirrors = TypeUtils.ofTypeMirrors(Arrays.asList(classElement, interfaceElement));
+     * // typeMirrors now contains the TypeMirror of MyClass and MyInterface
+     *
+     * List emptyList = TypeUtils.ofTypeMirrors(Collections.emptyList()); // returns an empty list
+     * }
+ * + * @param elements The collection of Elements to convert. Must not be null. + * @return A list of TypeMirrors derived from the given Elements. + */ + @Nonnull + @Immutable + static List ofTypeMirrors(Collection elements) { + return ofTypeMirrors(elements, EMPTY_PREDICATE_ARRAY); + } + + @Nonnull + @Immutable + static List ofTypeMirrors(Collection elements, Predicate... typeFilters) { + return isEmpty(elements) ? emptyList() : + unmodifiableList(elements.stream().map(Element::asType).filter(and(typeFilters)).collect(toList())); + } + + /** + * Converts an array of TypeMirrors to a List of TypeElements. + * If the input array is null or empty, returns an empty list. + * + *

Example Usage

+ *
{@code
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
+     *
+     * List typeElements = TypeUtils.ofTypeElements(classTypeMirror, interfaceTypeMirror);
+     * // typeElements now contains the TypeElement of MyClass and MyInterface
+     *
+     * List emptyList = TypeUtils.ofTypeElements(); // returns an empty list
+     * }
+ * + * @param types The array of TypeMirrors to convert. May be null or contain null elements. + * @return A List of TypeElements derived from the given TypeMirrors. + */ + @Nonnull + @Immutable + static List ofTypeElements(TypeMirror... types) { + return ofTypeElements(ofList(types)); + } + + /** + * Converts a collection of TypeMirrors to a list of TypeElements. + * Optionally applies an array of predicates to filter the resulting TypeElements. + * + *

Example Usage

+ *
{@code
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
+     *
+     * List typeElements = TypeUtils.ofTypeElements(Arrays.asList(classTypeMirror, interfaceTypeMirror));
+     * // typeElements now contains the TypeElement of MyClass and MyInterface
+     *
+     * List emptyList = TypeUtils.ofTypeElements(Collections.emptyList()); // returns an empty list
+     * }
+ * + * @param types The collection of TypeMirrors to convert. Must not be null. + * @return A list of TypeElements derived from the given TypeMirrors. + */ + @Nonnull + @Immutable + static List ofTypeElements(Collection types) { + return ofTypeElements(types, EMPTY_PREDICATE_ARRAY); + } + + /** + * Converts a collection of TypeMirrors to a list of TypeElements. + * Optionally applies an array of predicates to filter the resulting TypeElements. + * + *

Example Usage

+ *
{@code
+     * TypeMirror classTypeMirror = element.asType(); // represents a class like MyClass
+     * TypeMirror interfaceTypeMirror = element.asType(); // represents an interface like MyInterface
+     *
+     * List typeElements = TypeUtils.ofTypeElements(Arrays.asList(classTypeMirror, interfaceTypeMirror));
+     * // typeElements now contains the TypeElement of MyClass and MyInterface
+     *
+     * List emptyList = TypeUtils.ofTypeElements(Collections.emptyList()); // returns an empty list
+     * }
+ * + * @param types The collection of TypeMirrors to convert. Must not be null. + * @param typeFilters Optional predicates to filter the TypeElements. May be null or empty. + * @return A list of TypeElements derived from the given TypeMirrors, filtered by the provided predicates. + */ + @Nonnull + @Immutable + static List ofTypeElements(Collection types, Predicate... typeFilters) { + return isEmpty(types) ? emptyList() : unmodifiableList( + types.stream() + .map(TypeUtils::ofTypeElement) + .filter(Objects::nonNull) + .filter(and(typeFilters)).collect(toList()) + ); + } + + /** + * Converts an array of Elements to a List of DeclaredTypes. + * If the input array is null or empty, returns an empty list. + * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * Element interfaceElement = ...; // represents an interface like MyInterface
+     *
+     * List declaredTypes = TypeUtils.ofDeclaredTypes(classElement, interfaceElement);
+     * // declaredTypes now contains the DeclaredType of MyClass and MyInterface
+     *
+     * List emptyList = TypeUtils.ofDeclaredTypes(); // returns an empty list
+     * }
+ * + * @param elements the array of Elements to convert + * @return a List of DeclaredTypes derived from the given Elements + */ + @Nonnull + @Immutable + static List ofDeclaredTypes(Element... elements) { + return ofDeclaredTypes(ofList(elements)); + } + + /** + * Converts a collection of Elements to a list of DeclaredTypes. + * Optionally applies an array of predicates to filter the resulting DeclaredTypes. + * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * Element interfaceElement = ...; // represents an interface like MyInterface
+     *
+     * List declaredTypes = TypeUtils.ofDeclaredTypes(Arrays.asList(classElement, interfaceElement));
+     * // declaredTypes now contains the DeclaredType of MyClass and MyInterface
+     *
+     * List emptyList = TypeUtils.ofDeclaredTypes(Collections.emptyList()); // returns an empty list
+     * }
+ * + * @param elements The collection of Elements to convert. Must not be null. + * @return A list of DeclaredTypes derived from the given Elements. + */ + @Nonnull + @Immutable + static List ofDeclaredTypes(Collection elements) { + return ofDeclaredTypes(elements, EMPTY_PREDICATE_ARRAY); + } + + /** + * Converts a collection of Elements to a list of DeclaredTypes. + * Optionally applies an array of predicates to filter the resulting DeclaredTypes. + * + *

Example Usage

+ *
{@code
+     * Element classElement = ...; // represents a class like MyClass
+     * Element interfaceElement = ...; // represents an interface like MyInterface
+     *
+     * List declaredTypes = TypeUtils.ofDeclaredTypes(Arrays.asList(classElement, interfaceElement));
+     * // declaredTypes now contains the DeclaredType of MyClass and MyInterface
+     *
+     * List emptyList = TypeUtils.ofDeclaredTypes(Collections.emptyList()); // returns an empty list
+     * }
+ * + * @param elements The collection of Elements to convert. Must not be null. + * @param typeFilters Optional predicates to filter the DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes derived from the given Elements, filtered by the provided predicates. + */ + @Nonnull + @Immutable + static List ofDeclaredTypes(Collection elements, + Predicate... typeFilters) { + + if (isEmpty(elements)) { + return emptyList(); + } + + List declaredTypes = elements.stream() + .map(TypeUtils::ofTypeElement) + .filter(Objects::nonNull) + .map(Element::asType) + .map(TypeUtils::ofDeclaredType) + .filter(Objects::nonNull) + .filter(and(typeFilters)) + .collect(toList()); + + return declaredTypes.isEmpty() ? emptyList() : unmodifiableList(declaredTypes); + } + + /** + * Retrieves the TypeElement representing the superclass of the given TypeElement. + * + *

If the provided TypeElement is null or represents a class without a superclass + * (e.g., {@link Object}, an interface, or an enum), this method returns null. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = ...; // represents a class like MyClass which extends SomeBaseClass
+     * TypeElement superClass = TypeUtils.getTypeElementOfSuperclass(type);
+     * // superClass now represents SomeBaseClass if available
+     *
+     * TypeElement interfaceType = ...; // represents an interface
+     * TypeElement superClassForInterface = TypeUtils.getTypeElementOfSuperclass(interfaceType);
+     * // superClassForInterface will be null since interfaces do not have a superclass
+     *
+     * TypeElement objectType = processingEnv.getElementUtils().getTypeElement("java.lang.Object");
+     * TypeElement superClassForObject = TypeUtils.getTypeElementOfSuperclass(objectType);
+     * // superClassForObject will be null since Object has no superclass
+     * }
+ * + * @param type the TypeElement whose superclass is to be retrieved, may be null + * @return the TypeElement of the superclass if available; otherwise, null + */ + @Nullable + static TypeElement getTypeElementOfSuperclass(TypeElement type) { + return type == null ? null : ofTypeElement(type.getSuperclass()); + } + + /** + * Retrieves all TypeElements representing the superclasses and interfaces in the hierarchy of the given TypeElement. + * This includes both direct and indirect superclasses as well as implemented interfaces from the entire hierarchy. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List superTypes = TypeUtils.getAllTypeElementsOfSuperTypes(type);
+     * // superTypes now contains all superclasses and interfaces in the hierarchy of MyClass
+     *
+     * TypeElement interfaceType = processingEnv.getElementUtils().getTypeElement("com.example.MyInterface");
+     * List interfaceSuperTypes = TypeUtils.getAllTypeElementsOfSuperTypes(interfaceType);
+     * // interfaceSuperTypes now contains all superinterfaces of MyInterface
+     *
+     * List emptyList = TypeUtils.getAllTypeElementsOfSuperTypes(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement whose hierarchy is to be explored, may be null. + * @return A list of TypeElements representing all superclasses and interfaces in the hierarchy of the provided TypeElement. + * Returns an empty list if the input is null or no super types exist. + */ + @Nonnull + @Immutable + static List getAllTypeElementsOfSuperTypes(TypeElement type) { + return findAllTypeElementsOfSuperTypes(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves all TypeElements representing the superclasses in the hierarchy of the given TypeElement. + * This includes direct and indirect superclasses, but excludes interfaces. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List superclasses = TypeUtils.getAllTypeElementsOfSuperclasses(type);
+     * // superclasses now contains all superclass TypeElements in the hierarchy of MyClass
+     *
+     * List emptyList = TypeUtils.getAllTypeElementsOfSuperclasses(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement whose superclass hierarchy is to be explored, may be null. + * @return A list of TypeElements representing all superclasses in the hierarchy of the provided TypeElement. + * Returns an empty list if the input is null or no superclasses exist in the hierarchy. + */ + @Nonnull + @Immutable + static List getAllTypeElementsOfSuperclasses(TypeElement type) { + return findAllTypeElementsOfSuperclasses(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves the direct interface types implemented by the given TypeElement. + * This method only returns interfaces directly declared on the specified type, + * and does not include superinterfaces from the entire hierarchy. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List interfaces = TypeUtils.getTypeElementsOfInterfaces(type);
+     * // interfaces now contains the directly implemented interfaces of MyClass
+     *
+     * List emptyList = TypeUtils.getTypeElementsOfInterfaces(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement whose directly implemented interfaces are to be retrieved, may be null. + * @return A list of TypeElements representing the directly implemented interfaces. + * Returns an empty list if the input is null or no interfaces are directly implemented. + */ + @Nonnull + @Immutable + static List getTypeElementsOfInterfaces(TypeElement type) { + return findTypeElementsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves all TypeElements representing the interfaces implemented in the entire hierarchy of the given TypeElement. + * This includes both directly and indirectly implemented interfaces from superclasses and superinterfaces. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List interfaces = TypeUtils.getAllTypeElementsOfInterfaces(type);
+     * // interfaces now contains all interfaces implemented by MyClass, including those from superclasses
+     *
+     * List emptyList = TypeUtils.getAllTypeElementsOfInterfaces(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement whose interface hierarchy is to be explored, may be null. + * @return A list of TypeElements representing all implemented interfaces in the hierarchy of the provided TypeElement. + * Returns an empty list if the input is null or no interfaces are found in the hierarchy. + */ + @Nonnull + @Immutable + static List getAllTypeElementsOfInterfaces(TypeElement type) { + return findAllTypeElementsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves the directly associated TypeElements of the given TypeElement. + * This includes: + * - The type itself + * - Direct superclass (if any) + * - Directly implemented interfaces (if any) + * + *

This method does not traverse the entire hierarchy. It only includes direct relationships. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List directTypes = TypeUtils.getTypeElements(type);
+     * // directTypes contains:
+     * // - MyClass itself
+     * // - Its direct superclass (if any)
+     * // - Interfaces directly implemented by MyClass (if any)
+     *
+     * List emptyList = TypeUtils.getTypeElements(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement to retrieve directly associated types from, may be null. + * @return A list of TypeElements representing the directly associated types. + * Returns an empty list if the input is null or no direct associations exist. + */ + @Nonnull + @Immutable + static List getTypeElements(TypeElement type) { + return getTypeElements(type, true, false, true, true); + } + + /** + * Retrieves all TypeElements associated with the given TypeElement, including: + * - The type itself + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List allTypes = TypeUtils.getAllTypeElements(type);
+     * // allTypes contains:
+     * // - MyClass itself
+     * // - All superclasses in the hierarchy (e.g., Object, SomeBaseClass)
+     * // - All implemented interfaces, including those from superclasses and superinterfaces
+     *
+     * List emptyList = TypeUtils.getAllTypeElements(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement to retrieve all associated types from, may be null. + * @return A list of TypeElements representing all associated types in the hierarchy. + * Returns an empty list if the input is null or no types are found. + */ + @Nonnull + @Immutable + static List getAllTypeElements(TypeElement type) { + return getTypeElements(type, true, true, true, true); + } + + /** + * Retrieves a list of TypeElements associated with the given TypeElement based on the specified inclusion criteria. + * + *

+ * This method allows fine-grained control over which types are included in the result: + *

+ * + *
    + *
  • {@code includeSelf} - Whether to include the given TypeElement itself in the result.
  • + *
  • {@code includeHierarchicalTypes} - Whether to include types from the entire hierarchy (e.g., superclasses and interfaces).
  • + *
  • {@code includeSuperclass} - Whether to include direct or hierarchical superclasses based on the value of includeHierarchicalTypes.
  • + *
  • {@code includeSuperInterfaces} - Whether to include direct or hierarchical interfaces based on the value of includeHierarchicalTypes.
  • + *
+ * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     *
+     * // Get the type itself and all direct superclasses and interfaces
+     * List directTypes = TypeUtils.getTypeElements(type, false, false, true, true);
+     *
+     * // Get the type itself and all hierarchical superclasses and interfaces
+     * List hierarchicalTypes = TypeUtils.getTypeElements(type, true, true, true, true);
+     *
+     * // Get only the direct superclasses without including interfaces
+     * List superclassesOnly = TypeUtils.getTypeElements(type, false, false, true, false);
+     *
+     * // Get only the hierarchical interfaces
+     * List interfacesOnly = TypeUtils.getTypeElements(type, false, true, false, true);
+     * }
+ * + * @param type The TypeElement to find associated types for, may be null. + * @param includeSelf Whether to include the type itself in the result. + * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). + * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. + * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. + * @return A list of TypeElements representing the associated types according to the inclusion criteria. + * Returns an empty list if the input type is null or no matching types are found. + */ + @Nonnull + @Immutable + static List getTypeElements(TypeElement type, + boolean includeSelf, + boolean includeHierarchicalTypes, + boolean includeSuperClasses, + boolean includeSuperInterfaces) { + return findTypeElements(type, includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves the TypeElements representing the interfaces directly implemented by the given TypeElement. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include superinterfaces from the entire hierarchy. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List interfaces = TypeUtils.findTypeElementsOfInterfaces(type);
+     * // interfaces now contains the directly implemented interfaces of MyClass
+     *
+     * List emptyList = TypeUtils.findTypeElementsOfInterfaces(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement whose directly implemented interfaces are to be retrieved, may be null. + * @param interfaceFilters Optional predicates to filter the resulting TypeElements. May be null or empty. + * @return A list of TypeElements representing the directly implemented interfaces. + * Returns an empty list if the input is null or no interfaces are directly implemented. + */ + @Nonnull + @Immutable + static List findTypeElementsOfInterfaces(TypeElement type, Predicate... interfaceFilters) { + return findTypeElements(type, false, false, false, true, interfaceFilters); + } + + /** + * Retrieves all TypeElements representing the superclasses in the hierarchy of the given TypeElement. + * This includes direct and indirect superclasses, but excludes interfaces. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List superclasses = TypeUtils.findAllTypeElementsOfSuperclasses(type);
+     * // superclasses now contains all superclass TypeElements in the hierarchy of MyClass
+     *
+     * List emptyList = TypeUtils.findAllTypeElementsOfSuperclasses(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement whose superclass hierarchy is to be explored, may be null. + * @param typeFilters Optional predicates to filter the resulting TypeElements. May be null or empty. + * @return A list of TypeElements representing all superclasses in the hierarchy of the provided TypeElement. + * Returns an empty list if the input is null or no superclasses exist in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllTypeElementsOfSuperclasses(TypeElement type, Predicate... typeFilters) { + return findTypeElements(type, false, true, true, false, typeFilters); + } + + /** + * Retrieves all TypeElements representing the interfaces implemented in the entire hierarchy of the given TypeElement. + * This includes both directly and indirectly implemented interfaces from superclasses and superinterfaces. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List interfaces = TypeUtils.findAllTypeElementsOfInterfaces(type);
+     * // interfaces now contains all interfaces implemented by MyClass, including those from superclasses
+     *
+     * List emptyList = TypeUtils.findAllTypeElementsOfInterfaces(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement whose interface hierarchy is to be explored, may be null. + * @return A list of TypeElements representing all implemented interfaces in the hierarchy of the provided TypeElement. + * Returns an empty list if the input is null or no interfaces are found in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllTypeElementsOfInterfaces(TypeElement type, Predicate... interfaceFilters) { + return findTypeElements(type, false, true, false, true, interfaceFilters); + } + + /** + * Retrieves all TypeElements representing the superclasses and interfaces in the hierarchy of the given TypeElement. + * This includes both direct and indirect superclasses as well as implemented interfaces from the entire hierarchy. + * Optionally applies an array of predicates to filter the resulting TypeElements. + * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List superTypes = TypeUtils.findAllTypeElementsOfSuperTypes(type);
+     * // superTypes now contains all superclasses and interfaces in the hierarchy of MyClass
+     *
+     * List filteredSuperTypes = TypeUtils.findAllTypeElementsOfSuperTypes(type,
+     *     t -> t.getQualifiedName().toString().startsWith("com.example"));
+     * // filteredSuperTypes contains only those superTypes whose qualified names start with "com.example"
+     *
+     * List emptyList = TypeUtils.findAllTypeElementsOfSuperTypes(null); // returns an empty list
+     * }
+ * + * @param type The TypeElement whose hierarchy is to be explored, may be null. + * @param typeFilters Optional predicates to filter the resulting TypeElements. May be null or empty. + * @return A list of TypeElements representing all superclasses and interfaces in the hierarchy of the provided TypeElement, + * filtered by the provided predicates. Returns an empty list if the input is null or no types are found in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllTypeElementsOfSuperTypes(TypeElement type, Predicate... typeFilters) { + return findTypeElements(type, false, true, true, true, typeFilters); + } + + /** + * Finds and returns a list of TypeElements based on the specified criteria. + * + *

This method allows detailed control over which types are included in the result: + * - Whether to include the type itself + * - Whether to include hierarchical types (e.g., superclasses and interfaces) + * - Whether to include superclasses and/or interfaces based on the inclusion criteria

+ * + *

Example Usage

+ *
{@code
+     * TypeElement type = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     *
+     * // Get the type itself and all direct superclasses and interfaces
+     * List directTypes = TypeUtils.findTypeElements(type, true, false, true, true);
+     *
+     * // Get all types in the hierarchy including superclasses and interfaces
+     * List hierarchicalTypes = TypeUtils.findTypeElements(type, true, true, true, true);
+     *
+     * // Get only direct superclasses without including interfaces
+     * List superclassesOnly = TypeUtils.findTypeElements(type, false, false, true, false);
+     *
+     * // Get only interfaces from the entire hierarchy
+     * List interfacesOnly = TypeUtils.findTypeElements(type, false, true, false, true);
+     * }
+ * + * @param type The TypeElement to start the search from, may be null. + * @param includeSelf Whether to include the type itself in the result. + * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). + * @param includeSuperclass Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. + * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. + * @param typeFilters Optional predicates to filter the resulting TypeElements. May be null or empty. + * @return A list of TypeElements matching the specified criteria. + * Returns an empty list if the input type is null or no matching types are found. + * @throws IllegalArgumentException if any element of 'typeFilters' array is null. + */ + @Nonnull + @Immutable + static List findTypeElements(TypeElement type, + boolean includeSelf, + boolean includeHierarchicalTypes, + boolean includeSuperclass, + boolean includeSuperInterfaces, + Predicate... typeFilters) throws IllegalArgumentException { + if (type == null) { + return emptyList(); + } + assertNoNullElements(typeFilters, () -> "Any element of 'typeFilters' array must not be null"); + return typeElementFinder(type, includeSelf, includeHierarchicalTypes, includeSuperclass, includeSuperInterfaces).findTypes(typeFilters); + } + + /** + * Retrieves the declared type of the superclass for the given Element. + * If the provided Element is null or does not represent a type with a superclass, + * this method will return null. + * + *

Example Usage

+ *
{@code
+     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * DeclaredType superClassType = TypeUtils.getDeclaredTypeOfSuperclass(typeElement);
+     * // superClassType now contains the DeclaredType of the superclass of MyClass, if available
+     *
+     * Element interfaceElement = processingEnv.getElementUtils().getTypeElement("com.example.MyInterface");
+     * DeclaredType superClassTypeForInterface = TypeUtils.getDeclaredTypeOfSuperclass(interfaceElement);
+     * // superClassTypeForInterface will be null since interfaces do not have a superclass
+     *
+     * DeclaredType nullCase = TypeUtils.getDeclaredTypeOfSuperclass(null);
+     * // nullCase will be null since the input is null
+     * }
+ * + * @param typeElement the Element to retrieve the superclass declared type from, may be null + * @return the DeclaredType representing the superclass of the given Element, or null if none exists + */ + @Nullable + static DeclaredType getDeclaredTypeOfSuperclass(Element typeElement) { + return typeElement == null ? null : getDeclaredTypeOfSuperclass(typeElement.asType()); + } + + /** + * Retrieves the declared type of the superclass for the given TypeMirror. + * If the provided TypeMirror is null or does not represent a type with a superclass, + * this method will return null. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
+     * DeclaredType superClassType = TypeUtils.getDeclaredTypeOfSuperclass(type);
+     * // superClassType contains the DeclaredType of the superclass if available
+     *
+     * DeclaredType nullCase = TypeUtils.getDeclaredTypeOfSuperclass(null);
+     * // nullCase will be null since the input is null
+     * }
+ * + * @param type the TypeMirror to retrieve the superclass declared type from, may be null + * @return the DeclaredType representing the superclass of the given TypeMirror, or null if none exists + */ + @Nullable + static DeclaredType getDeclaredTypeOfSuperclass(TypeMirror type) { + TypeElement superType = getTypeElementOfSuperclass(ofTypeElement(type)); + return superType == null ? null : ofDeclaredType(superType.asType()); + } + + /** + * Retrieves a list of declared types representing the interfaces directly implemented by the given Element. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include interfaces from superclasses or superinterfaces. + * + *

Example Usage

+ *
{@code
+     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List interfaces = TypeUtils.getDeclaredTypesOfInterfaces(typeElement);
+     * // interfaces now contains the directly implemented interfaces of MyClass
+     *
+     * List emptyList = TypeUtils.getDeclaredTypesOfInterfaces(null); // returns an empty list
+     * }
+ * + * @param element The Element whose directly implemented interfaces are to be retrieved, may be null. + * @return A list of DeclaredTypes representing the interfaces directly implemented by the given Element. + * Returns an empty list if the input is null or no interfaces are directly implemented. + */ + @Nonnull + @Immutable + static List getDeclaredTypesOfInterfaces(Element element) { + return element == null ? emptyList() : findDeclaredTypesOfInterfaces(element.asType(), EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of declared types representing the interfaces directly implemented by the given TypeMirror. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include interfaces from superclasses or superinterfaces. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
+     * List interfaces = TypeUtils.getDeclaredTypesOfInterfaces(type);
+     * // interfaces now contains the directly implemented interfaces of the given type
+     *
+     * List emptyList = TypeUtils.getDeclaredTypesOfInterfaces(null); // returns an empty list
+     * }
+ * + * @param type The TypeMirror whose directly implemented interfaces are to be retrieved, may be null. + * @return A list of DeclaredTypes representing the interfaces directly implemented by the given TypeMirror. + * Returns an empty list if the input is null or no interfaces are directly implemented. + */ + @Nonnull + @Immutable + static List getDeclaredTypesOfInterfaces(TypeMirror type) { + return findDeclaredTypesOfInterfaces(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes representing all superclasses in the hierarchy of the given Element. + * This includes both direct and indirect superclasses, traversing up through the entire type hierarchy. + * If the provided Element is null or does not have any superclasses, an empty list is returned. + * + *

Example Usage

+ *
{@code
+     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List superclasses = TypeUtils.getAllDeclaredTypesOfSuperclasses(typeElement);
+     * // superclasses now contains all superclass DeclaredTypes in the hierarchy of MyClass
+     *
+     * List emptyList = TypeUtils.getAllDeclaredTypesOfSuperclasses(null); // returns an empty list
+     * }
+ * + * @param type The Element to retrieve superclass declared types from, may be null. + * @return A list of DeclaredTypes representing all superclasses in the hierarchy of the provided Element. + * Returns an empty list if the input is null or no superclasses exist in the hierarchy. + */ + @Nonnull + @Immutable + static List getAllDeclaredTypesOfSuperclasses(Element type) { + return type == null ? emptyList() : findAllDeclaredTypesOfSuperclasses(type.asType(), EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes representing all superclasses in the hierarchy of the given TypeMirror. + * This includes both direct and indirect superclasses, traversing up through the entire type hierarchy. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
+     * List superclasses = TypeUtils.getAllDeclaredTypesOfSuperclasses(type);
+     * // superclasses now contains all superclass DeclaredTypes in the hierarchy of the given type
+     *
+     * List emptyList = TypeUtils.getAllDeclaredTypesOfSuperclasses(null); // returns an empty list
+     * }
+ * + * @param type The TypeMirror whose superclass hierarchy is to be explored, may be null. + * @return A list of DeclaredTypes representing all superclasses in the hierarchy of the provided TypeMirror. + * Returns an empty list if the input is null or no superclasses exist in the hierarchy. + */ + @Nonnull + @Immutable + static List getAllDeclaredTypesOfSuperclasses(TypeMirror type) { + return findAllDeclaredTypesOfSuperclasses(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes representing all interfaces implemented in the entire hierarchy + * of the given Element. This includes both directly and indirectly implemented interfaces from + * superclasses and superinterfaces. + * + *

Example Usage

+ *
{@code
+     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List interfaces = TypeUtils.getAllDeclaredTypesOfInterfaces(typeElement);
+     * // interfaces now contains all interfaces implemented by MyClass, including those from superclasses
+     *
+     * List emptyList = TypeUtils.getAllDeclaredTypesOfInterfaces(null); // returns an empty list
+     * }
+ * + * @param type The Element whose interface hierarchy is to be explored, may be null. + * @return A list of DeclaredTypes representing all implemented interfaces in the hierarchy of + * the provided Element. Returns an empty list if the input is null or no interfaces + * are found in the hierarchy. + */ + @Nonnull + @Immutable + static List getAllDeclaredTypesOfInterfaces(Element type) { + return type == null ? emptyList() : findAllDeclaredTypesOfInterfaces(type.asType(), EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes representing all interfaces implemented in the entire hierarchy + * of the given TypeMirror. This includes both directly and indirectly implemented interfaces from + * superclasses and superinterfaces. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
+     * List interfaces = TypeUtils.getAllDeclaredTypesOfInterfaces(type);
+     * // interfaces now contains all interfaces implemented by the given type, including those from superclasses
+     *
+     * List emptyList = TypeUtils.getAllDeclaredTypesOfInterfaces(null); // returns an empty list
+     * }
+ * + * @param type The TypeMirror whose interface hierarchy is to be explored, may be null. + * @return A list of DeclaredTypes representing all implemented interfaces in the hierarchy of + * the provided TypeMirror. Returns an empty list if the input is null or no interfaces + * are found in the hierarchy. + */ + @Nonnull + @Immutable + static List getAllDeclaredTypesOfInterfaces(TypeMirror type) { + return findAllDeclaredTypesOfInterfaces(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes representing all superclasses and interfaces in the hierarchy + * of the given Element. This includes both direct and indirect superclasses as well as implemented + * interfaces from the entire hierarchy. + * + *

Example Usage

+ *
{@code
+     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List superTypes = TypeUtils.getAllDeclaredTypesOfSuperTypes(typeElement);
+     * // superTypes now contains all superclasses and interfaces in the hierarchy of MyClass
+     *
+     * List emptyList = TypeUtils.getAllDeclaredTypesOfSuperTypes(null); // returns an empty list
+     * }
+ * + * @param type The Element to retrieve super types from, may be null. + * @return A list of DeclaredTypes representing all superclasses and interfaces in the hierarchy + * of the provided Element. Returns an empty list if the input is null or no types are found. + */ + @Nonnull + @Immutable + static List getAllDeclaredTypesOfSuperTypes(Element type) { + return type == null ? emptyList() : findAllDeclaredTypesOfSuperTypes(type.asType(), EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes representing all superclasses and interfaces in the hierarchy + * of the given TypeMirror. This includes both direct and indirect superclasses as well as implemented + * interfaces from the entire hierarchy. + * + *

Example Usage

+ *
{@code
+     * TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(typeElement);
+     * List superTypes = TypeUtils.getAllDeclaredTypesOfSuperTypes(type);
+     * // superTypes now contains all superclasses and interfaces in the hierarchy of the given type
+     *
+     * List emptyList = TypeUtils.getAllDeclaredTypesOfSuperTypes(null); // returns an empty list
+     * }
+ * + * @param type The TypeMirror whose hierarchy is to be explored, may be null. + * @return A list of DeclaredTypes representing all superclasses and interfaces in the hierarchy of + * the provided TypeMirror. Returns an empty list if the input is null or no types are found. + */ + @Nonnull + @Immutable + static List getAllDeclaredTypesOfSuperTypes(TypeMirror type) { + return findAllDeclaredTypesOfSuperTypes(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes representing all associated types of the given Element. + * This includes: + * - The type itself (if it is a declared type) + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces implemented by the type + * + *

Example Usage

+ *
{@code
+     * Element typeElement = processingEnv.getElementUtils().getTypeElement("com.example.MyClass");
+     * List allTypes = TypeUtils.getAllDeclaredTypes(typeElement);
+     * // allTypes now contains MyClass itself, its superclasses, and all implemented interfaces
+     *
+     * List emptyList = TypeUtils.getAllDeclaredTypes(null); // returns an empty list
+     * }
+ * + * @param type The Element to retrieve associated DeclaredTypes from, may be null. + * @return A list of DeclaredTypes derived from the given Element. Returns an empty list if + * the input is null or no DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List getAllDeclaredTypes(Element type) { + return type == null ? emptyList() : findAllDeclaredTypes(type.asType(), EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy of the given TypeMirror. + * This includes: + * - The type itself (if it is a declared type) + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces implemented by the type + * + * @param type The TypeMirror to retrieve associated DeclaredTypes from, may be null. + * @return A list of DeclaredTypes representing all associated types in the hierarchy of the provided TypeMirror. + * Returns an empty list if the input is null or no DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List getAllDeclaredTypes(TypeMirror type) { + return findAllDeclaredTypes(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of DeclaredTypes associated with the given Element based on the specified inclusion criteria. + * A DeclaredType typically refers to a type that is explicitly declared in the code, such as classes, + * interfaces, enums, or annotation types. + * + * @param type The Element to retrieve DeclaredTypes from, may be null. + * @param includeSelf Whether to include the type itself in the result. + * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). + * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. + * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. + * @return A list of DeclaredTypes derived from the given Element according to the inclusion criteria. + * Returns an empty list if the input is null or no DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List getDeclaredTypes(Element type, + boolean includeSelf, + boolean includeHierarchicalTypes, + boolean includeSuperClasses, + boolean includeSuperInterfaces) { + return getDeclaredTypes(type.asType(), includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces); + } + + /** + * Retrieves a list of DeclaredTypes associated with the given TypeMirror based on the specified inclusion criteria. + * A DeclaredType typically refers to a type that is explicitly declared in the code, such as classes, + * interfaces, enums, or annotation types. + * + * @param type The TypeMirror to retrieve DeclaredTypes from, may be null. + * @param includeSelf Whether to include the type itself in the result. + * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). + * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. + * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. + * @return A list of DeclaredTypes derived from the given TypeMirror according to the inclusion criteria. + * Returns an empty list if the input is null or no DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List getDeclaredTypes(TypeMirror type, + boolean includeSelf, + boolean includeHierarchicalTypes, + boolean includeSuperClasses, + boolean includeSuperInterfaces) { + return findDeclaredTypes(type, includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces, EMPTY_PREDICATE_ARRAY); + } + + + /** + * Finds and returns a list of DeclaredTypes associated with the given TypeMirror, + * excluding any types that match the specified excludedTypes. + * + * @param type The TypeMirror to find associated DeclaredTypes from, may be null. + * @param excludedTypes The array of Types to exclude from the result. May be null or empty. + * @return A list of DeclaredTypes derived from the given TypeMirror, excluding the specified types. + * Returns an empty list if the input type is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findDeclaredTypes(TypeMirror type, Type... excludedTypes) { + return type == null ? emptyList() : findDeclaredTypes(ofTypeElement(type), excludedTypes); + } + + /** + * Finds and returns a list of DeclaredTypes associated with the given Element, + * excluding any types that match the specified excludedTypes. + * + * @param type The Element to find associated DeclaredTypes from, may be null. + * @param excludedTypes The array of Types to exclude from the result. May be null or empty. + * @return A list of DeclaredTypes derived from the given Element, excluding the specified types. + * Returns an empty list if the input type is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findDeclaredTypes(Element type, Type... excludedTypes) { + return type == null ? emptyList() : findDeclaredTypes(type, getTypeNames(excludedTypes)); + } + + /** + * Finds and returns a list of DeclaredTypes associated with the given TypeMirror, + * excluding any types whose names match the specified excludedTypeNames. + * + * @param type The TypeMirror to find associated DeclaredTypes from, may be null. + * @param excludedTypeNames An array of fully qualified type names to exclude from the result. + * May be null or empty. + * @return A list of DeclaredTypes derived from the given TypeMirror, excluding the specified types. + * Returns an empty list if the input type is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findDeclaredTypes(TypeMirror type, CharSequence... excludedTypeNames) { + return type == null ? emptyList() : findDeclaredTypes(ofTypeElement(type), excludedTypeNames); + } + + /** + * Finds and returns a list of DeclaredTypes associated with the given Element, + * excluding any types whose names match the specified excludedTypeNames. + * + * @param type The Element to find associated DeclaredTypes from, may be null. + * @param excludedTypeNames An array of fully qualified type names to exclude from the result. + * May be null or empty. + * @return A list of DeclaredTypes derived from the given Element, excluding the specified types. + * Returns an empty list if the input is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findDeclaredTypes(Element type, CharSequence... excludedTypeNames) { + return type == null ? emptyList() : findDeclaredTypes(type, false, false, true, true, t -> !contains(excludedTypeNames, t.toString())); + } + + /** + * Retrieves a list of DeclaredTypes representing the interfaces directly implemented by the given Element. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include interfaces from superclasses or superinterfaces. + * + * @param type The Element whose directly implemented interfaces are to be retrieved, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes representing the interfaces directly implemented by the given Element. + * Returns an empty list if the input is null or no interfaces are directly implemented. + */ + @Nonnull + @Immutable + static List findDeclaredTypesOfInterfaces(Element type, Predicate... typeFilters) { + return type == null ? emptyList() : findDeclaredTypesOfInterfaces(type.asType(), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing the interfaces directly implemented by the given TypeMirror. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include interfaces from superclasses or superinterfaces. + * + * @param type The TypeMirror whose directly implemented interfaces are to be retrieved, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes representing the interfaces directly implemented by the given TypeMirror. + * Returns an empty list if the input is null or no interfaces are directly implemented. + */ + @Nonnull + @Immutable + static List findDeclaredTypesOfInterfaces(TypeMirror type, Predicate... typeFilters) { + return type == null ? emptyList() : ofDeclaredTypes(getTypeElementsOfInterfaces(ofTypeElement(type)), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing all superclasses in the hierarchy of the given Element. + * This includes both direct and indirect superclasses, traversing up through the entire type hierarchy. + * Optionally applies an array of predicates to filter the resulting DeclaredTypes. + * + * @param type The Element to retrieve superclass declared types from, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes representing all superclasses in the hierarchy of the provided Element. + * Returns an empty list if the input is null or no superclasses exist in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypesOfSuperclasses(Element type, Predicate... typeFilters) { + return type == null ? emptyList() : findAllDeclaredTypesOfSuperclasses(type.asType(), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing all superclasses in the hierarchy of the given TypeMirror. + * This includes both direct and indirect superclasses, traversing up through the entire type hierarchy. + * Optionally applies an array of predicates to filter the resulting DeclaredTypes. + * + * @param type The TypeMirror whose superclass hierarchy is to be explored, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes representing all superclasses in the hierarchy of the provided TypeMirror, + * filtered by the provided predicates. Returns an empty list if the input is null or no superclasses + * exist in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypesOfSuperclasses(TypeMirror type, Predicate... typeFilters) { + return type == null ? emptyList() : ofDeclaredTypes(getAllTypeElementsOfSuperclasses(ofTypeElement(type)), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing all interfaces implemented in the entire hierarchy + * of the given Element. This includes both directly and indirectly implemented interfaces from + * superclasses and superinterfaces. Optionally applies an array of predicates to filter the resulting DeclaredTypes. + * + * @param type The Element whose interface hierarchy is to be explored, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes representing all implemented interfaces in the hierarchy of + * the provided Element, filtered by the provided predicates. Returns an empty list if + * the input is null or no interfaces are found in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypesOfInterfaces(Element type, Predicate... typeFilters) { + return type == null ? emptyList() : findAllDeclaredTypesOfInterfaces(type.asType(), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing all interfaces implemented in the entire hierarchy + * of the given TypeMirror. This includes both directly and indirectly implemented interfaces from + * superclasses and superinterfaces. Optionally applies an array of predicates to filter the resulting DeclaredTypes. + * + * @param type The TypeMirror whose interface hierarchy is to be explored, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes representing all implemented interfaces in the hierarchy of + * the provided TypeMirror, filtered by the provided predicates. Returns an empty list if + * the input is null or no interfaces are found in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypesOfInterfaces(TypeMirror type, Predicate... typeFilters) { + return type == null ? emptyList() : ofDeclaredTypes(getAllTypeElementsOfInterfaces(ofTypeElement(type)), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing all superclasses and interfaces in the hierarchy + * of the given Element. This includes both direct and indirect superclasses as well as implemented + * interfaces from the entire hierarchy. Optionally applies an array of predicates to filter the resulting DeclaredTypes. + * + * @param type The Element to retrieve superclass and interface declared types from, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes representing all superclasses and interfaces in the hierarchy of + * the provided Element, filtered by the provided predicates. Returns an empty list if the + * input is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypesOfSuperTypes(Element type, Predicate... typeFilters) { + return type == null ? emptyList() : findAllDeclaredTypesOfSuperTypes(type.asType(), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing all superclasses and interfaces in the hierarchy + * of the given TypeMirror. This includes both direct and indirect superclasses as well as implemented + * interfaces from the entire hierarchy. Optionally applies an array of predicates to filter the resulting DeclaredTypes. + * + * @param type The TypeMirror whose superclass and interface hierarchy is to be explored, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes representing all superclasses and interfaces in the hierarchy of + * the provided TypeMirror, filtered by the provided predicates. Returns an empty list if the + * input is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypesOfSuperTypes(TypeMirror type, Predicate... typeFilters) { + return type == null ? emptyList() : ofDeclaredTypes(getAllTypeElementsOfSuperTypes(ofTypeElement(type)), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy + * of the given TypeMirror, excluding any types that match the specified excludedTypes. + * This includes: + * - The type itself (if it is a declared type) + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces implemented by the type + * + * @param type The TypeMirror to retrieve associated DeclaredTypes from, may be null. + * @param excludedTypes The array of Types to exclude from the result. May be null or empty. + * @return A list of DeclaredTypes derived from the given TypeMirror, excluding the specified types. + * Returns an empty list if the input is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypes(TypeMirror type, Type... excludedTypes) { + return type == null ? emptyList() : findAllDeclaredTypes(ofTypeElement(type), excludedTypes); + } + + /** + * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy + * of the given Element, excluding any types that match the specified excludedTypes. + * This includes: + * - The type itself (if it is a declared type) + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces implemented by the type + * + * @param type The Element to retrieve associated DeclaredTypes from, may be null. + * @param excludedTypes The array of Types to exclude from the result. May be null or empty. + * @return A list of DeclaredTypes derived from the given Element, excluding the specified types. + * Returns an empty list if the input is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypes(Element type, Type... excludedTypes) { + return type == null ? emptyList() : findAllDeclaredTypes(type, getTypeNames(excludedTypes)); + } + + /** + * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy + * of the given TypeMirror, excluding any types whose names match the specified excludedTypeNames. + * This includes: + * - The type itself (if it is a declared type) + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces implemented by the type + * + * @param type The TypeMirror to retrieve associated DeclaredTypes from, may be null. + * @param excludedTypeNames An array of fully qualified type names to exclude from the result. + * May be null or empty. + * @return A list of DeclaredTypes derived from the given TypeMirror, excluding the specified types. + * Returns an empty list if the input type is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypes(TypeMirror type, CharSequence... excludedTypeNames) { + return type == null ? emptyList() : findAllDeclaredTypes(ofTypeElement(type), excludedTypeNames); + } + + /** + * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy + * of the given Element, excluding any types whose names match the specified excludedTypeNames. + * This includes: + * - The type itself (if it is a declared type) + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces implemented by the type + * + * @param type The Element to retrieve associated DeclaredTypes from, may be null. + * @param excludedTypeNames An array of fully qualified type names to exclude from the result. + * May be null or empty. + * @return A list of DeclaredTypes derived from the given Element, excluding the specified types. + * Returns an empty list if the input is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypes(Element type, CharSequence... excludedTypeNames) { + return type == null ? emptyList() : findAllDeclaredTypes(type, t -> !contains(excludedTypeNames, t.toString())); + } + + /** + * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy + * of the given Element, filtered by the provided predicates. + * This includes: + * - The type itself (if it is a declared type) + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces implemented by the type + * + * @param type The Element to retrieve associated DeclaredTypes from, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes derived from the given Element, filtered by the provided predicates. + * Returns an empty list if the input is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypes(Element type, Predicate... typeFilters) { + return type == null ? emptyList() : findAllDeclaredTypes(type.asType(), typeFilters); + } + + /** + * Retrieves a list of DeclaredTypes representing all associated types in the hierarchy + * of the given TypeMirror, filtered by the provided predicates. + * This includes: + * - The type itself (if it is a declared type) + * - Direct and hierarchical superclasses + * - Direct and hierarchical interfaces implemented by the type + * + * @param type The TypeMirror to retrieve associated DeclaredTypes from, may be null. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes derived from the given TypeMirror, filtered by the provided predicates. + * Returns an empty list if the input is null or no matching DeclaredTypes are found. + */ + @Nonnull + @Immutable + static List findAllDeclaredTypes(TypeMirror type, Predicate... typeFilters) { + return type == null ? emptyList() : ofDeclaredTypes(getAllTypeElements(ofTypeElement(type)), typeFilters); + } + + /** + * Finds and returns a list of DeclaredTypes associated with the given Element based on the specified criteria. + * + *

A DeclaredType typically refers to a type that is explicitly declared in the code, such as classes, + * interfaces, enums, or annotation types. This method allows filtering based on whether to include: + * - The type itself + * - Direct or hierarchical superclasses (based on the includeHierarchicalTypes flag) + * - Direct or hierarchical interfaces (based on the includeHierarchicalTypes flag) + * + * @param type The Element to find associated DeclaredTypes from. May be null. + * @param includeSelf Whether to include the type itself in the result. + * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). + * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. + * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes matching the specified criteria. + * Returns an empty list if the input type is null or no matching types are found. + * @throws IllegalArgumentException if any element of 'typeFilters' array is null. + */ + @Nonnull + @Immutable + static List findDeclaredTypes(Element type, + boolean includeSelf, + boolean includeHierarchicalTypes, + boolean includeSuperClasses, + boolean includeSuperInterfaces, + Predicate... typeFilters) { + return type == null ? emptyList() : findDeclaredTypes(type.asType(), includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces, typeFilters); + } + + /** + * Finds and returns a list of DeclaredTypes associated with the given TypeMirror based on the specified criteria. + * + *

A DeclaredType typically refers to a type that is explicitly declared in the code, such as classes, + * interfaces, enums, or annotation types. This method allows filtering based on whether to include: + * - The type itself + * - Direct or hierarchical superclasses (based on the includeHierarchicalTypes flag) + * - Direct or hierarchical interfaces (based on the includeHierarchicalTypes flag) + * + * @param type The TypeMirror to find associated DeclaredTypes from. May be null. + * @param includeSelf Whether to include the type itself in the result. + * @param includeHierarchicalTypes Whether to include types from the entire hierarchy (e.g., superclasses and interfaces). + * @param includeSuperClasses Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes. + * @param includeSuperInterfaces Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes. + * @param typeFilters Optional predicates to filter the resulting DeclaredTypes. May be null or empty. + * @return A list of DeclaredTypes matching the specified criteria. + * Returns an empty list if the input type is null or no matching types are found. + * @throws IllegalArgumentException if any element of 'typeFilters' array is null. + */ + @Nonnull + @Immutable + static List findDeclaredTypes(TypeMirror type, + boolean includeSelf, + boolean includeHierarchicalTypes, + boolean includeSuperClasses, + boolean includeSuperInterfaces, + Predicate... typeFilters) { + return type == null ? emptyList() : ofDeclaredTypes(getTypeElements(ofTypeElement(type), includeSelf, includeHierarchicalTypes, includeSuperClasses, includeSuperInterfaces), typeFilters); + } + + /** + * Retrieves a list of TypeMirrors representing the interfaces directly implemented by the given TypeMirror. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include interfaces from superclasses or superinterfaces. + * + * @param type The TypeMirror whose directly implemented interfaces are to be retrieved, may be null. + * @return A list of TypeMirrors representing the interfaces directly implemented by the given TypeMirror. + * Returns an empty list if the input is null or no interfaces are directly implemented. + */ + @Nonnull + @Immutable + static List getTypeMirrorsOfInterfaces(TypeMirror type) { + return type == null ? emptyList() : findTypeMirrorsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of TypeMirrors representing the interfaces directly implemented by the given TypeElement. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include interfaces from superclasses or superinterfaces. + * + * @param type The TypeElement whose directly implemented interfaces are to be retrieved, may be null. + * @return A list of TypeMirrors representing the interfaces directly implemented by the given TypeElement. + * Returns an empty list if the input is null or no interfaces are directly implemented. + */ + @Nonnull + @Immutable + static List getTypeMirrorsOfInterfaces(TypeElement type) { + return type == null ? emptyList() : findTypeMirrorsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of TypeMirrors representing the interfaces directly implemented by the given TypeMirror. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include interfaces from superclasses or superinterfaces. + * + * @param type The TypeMirror whose directly implemented interfaces are to be retrieved, may be null. + * @param interfaceFilters Optional predicates to filter the resulting TypeMirrors. May be null or empty. + * @return A list of TypeMirrors representing the interfaces directly implemented by the given TypeMirror, + * filtered by the provided predicates. Returns an empty list if the input is null or no interfaces + * are directly implemented. + */ + @Nonnull + @Immutable + static List findTypeMirrorsOfInterfaces(TypeMirror type, Predicate... interfaceFilters) { + return type == null ? emptyList() : findTypeMirrorsOfInterfaces(ofTypeElement(type), interfaceFilters); + } + + /** + * Retrieves a list of TypeMirrors representing the interfaces directly implemented by the given TypeElement. + * This method only returns interfaces that are directly declared on the specified type, + * and does not include interfaces from superclasses or superinterfaces. + * + * @param type The TypeElement whose directly implemented interfaces are to be retrieved, may be null. + * @param interfaceFilters Optional predicates to filter the resulting TypeMirrors. May be null or empty. + * @return A list of TypeMirrors representing the interfaces directly implemented by the given TypeElement, + * filtered by the provided predicates. Returns an empty list if the input is null or no interfaces + * are directly implemented. + */ + @Nonnull + @Immutable + static List findTypeMirrorsOfInterfaces(TypeElement type, Predicate... interfaceFilters) { + if (type == null) { + return emptyList(); + } + List typeMirrors = getTypeElementsOfInterfaces(type).stream() + .map(TypeElement::asType) + .filter(and(interfaceFilters)) + .collect(toList()); + return typeMirrors.isEmpty() ? emptyList() : typeMirrors; + } + + /** + * Retrieves a list of TypeMirrors representing all interfaces implemented in the entire hierarchy + * of the given TypeMirror. This includes both directly and indirectly implemented interfaces from + * superclasses and superinterfaces. + * + * @param type The TypeMirror whose interface hierarchy is to be explored, may be null. + * @return A list of TypeMirrors representing all implemented interfaces in the hierarchy of + * the provided TypeMirror. Returns an empty list if the input is null or no interfaces + * are found in the hierarchy. + */ + @Nonnull + @Immutable + static List getAllTypeMirrorsOfInterfaces(TypeMirror type) { + return findAllTypeMirrorsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of TypeMirrors representing all interfaces implemented in the entire hierarchy + * of the given TypeElement. This includes both directly and indirectly implemented interfaces from + * superclasses and superinterfaces. + * + * @param type The TypeElement whose interface hierarchy is to be explored, may be null. + * @return A list of TypeMirrors representing all implemented interfaces in the hierarchy of + * the provided TypeElement. Returns an empty list if the input is null or no interfaces + * are found in the hierarchy. + */ + @Nonnull + @Immutable + static List getAllTypeMirrorsOfInterfaces(TypeElement type) { + return findAllTypeMirrorsOfInterfaces(type, EMPTY_PREDICATE_ARRAY); + } + + /** + * Retrieves a list of TypeMirrors representing all interfaces implemented in the entire hierarchy + * of the given TypeMirror. This includes both directly and indirectly implemented interfaces from + * superclasses and superinterfaces. Optionally applies an array of predicates to filter the resulting TypeMirrors. + * + * @param type The TypeMirror whose interface hierarchy is to be explored, may be null. + * @param interfaceFilters Optional predicates to filter the resulting TypeMirrors. May be null or empty. + * @return A list of TypeMirrors representing all implemented interfaces in the hierarchy of + * the provided TypeMirror, filtered by the provided predicates. Returns an empty list if + * the input is null or no interfaces are found in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllTypeMirrorsOfInterfaces(TypeMirror type, Predicate... interfaceFilters) { + return type == null ? emptyList() : findAllTypeMirrorsOfInterfaces(ofTypeElement(type), interfaceFilters); + } + + /** + * Retrieves a list of TypeMirrors representing all interfaces implemented in the entire hierarchy + * of the given TypeElement. This includes both directly and indirectly implemented interfaces from + * superclasses and superinterfaces. Optionally applies an array of predicates to filter the resulting TypeMirrors. + * + * @param type The TypeElement whose interface hierarchy is to be explored, may be null. + * @param interfaceFilters Optional predicates to filter the resulting TypeMirrors. May be null or empty. + * @return A list of TypeMirrors representing all implemented interfaces in the hierarchy of + * the provided TypeElement, filtered by the provided predicates. Returns an empty list if + * the input is null or no interfaces are found in the hierarchy. + */ + @Nonnull + @Immutable + static List findAllTypeMirrorsOfInterfaces(TypeElement type, Predicate... interfaceFilters) { + if (type == null) { + return emptyList(); + } + List typeMirrors = getAllTypeElementsOfInterfaces(type).stream() + .map(TypeElement::asType) + .filter(and(interfaceFilters)) + .collect(toList()); + return typeMirrors.isEmpty() ? emptyList() : typeMirrors; + } + + /** + * Finds and returns the TypeMirror representing the specified interface type from the given Element. + * If the provided Element or interfaceType is null, returns null. + * + * @param type The Element to find the interface TypeMirror from. May be null. + * @param interfaceType The Type representing the interface to search for. May be null. + * @return The TypeMirror of the specified interface if found; otherwise, null. + */ + @Nullable + static TypeMirror findInterfaceTypeMirror(Element type, Type interfaceType) { + return findInterfaceTypeMirror(type, interfaceType.getTypeName()); + } + + /** + * Finds and returns the TypeMirror representing the specified interface type from the given TypeMirror. + * If the provided TypeMirror or interfaceType is null, returns null. + * + * @param type The TypeMirror to find the interface TypeMirror from. May be null. + * @param interfaceType The Type representing the interface to search for. May be null. + * @return The TypeMirror of the specified interface if found; otherwise, null. + */ + @Nullable + static TypeMirror findInterfaceTypeMirror(TypeMirror type, Type interfaceType) { + return findInterfaceTypeMirror(type, interfaceType.getTypeName()); + } + + /** + * Finds and returns the TypeMirror representing the specified interface type from the given Element. + * If the provided Element or interfaceClassName is null, returns null. + * + * @param type The Element to find the interface TypeMirror from. May be null. + * @param interfaceClassName The fully qualified class name of the interface to search for. May be null. + * @return The TypeMirror of the specified interface if found; otherwise, null. + */ + @Nullable + static TypeMirror findInterfaceTypeMirror(Element type, CharSequence interfaceClassName) { + return type == null ? null : findInterfaceTypeMirror(type.asType(), interfaceClassName); + } + + /** + * Finds and returns the TypeMirror representing the specified interface type from the given TypeMirror. + * If the provided TypeMirror or interfaceClassName is null, returns null. + * + *

This method searches through all interfaces implemented in the entire hierarchy of the given TypeMirror, + * including both directly and indirectly implemented interfaces from superclasses and superinterfaces. + * + * @param type The TypeMirror to find the interface TypeMirror from. May be null. + * @param interfaceClassName The fully qualified class name of the interface to search for. May be null. + * @return The TypeMirror of the specified interface if found; otherwise, null. + */ + @Nullable + static TypeMirror findInterfaceTypeMirror(TypeMirror type, CharSequence interfaceClassName) { + return filterFirst(getAllTypeMirrorsOfInterfaces(type), t -> isSameType(t, interfaceClassName)); + } + + /** + * Converts an array of Type objects to a list of TypeMirror instances using the provided ProcessingEnvironment. + * If the input array is null or empty, returns an empty list. + * + * @param processingEnv The ProcessingEnvironment used to resolve TypeMirrors. Must not be null. + * @param types The array of Type objects to convert. May contain null elements which will be ignored. + * @return A list of TypeMirror instances derived from the given Types. Returns an empty list if the input array is null or empty, + * or if no valid TypeMirror instances could be resolved. + */ + @Nonnull + @Immutable + static List getTypeMirrors(ProcessingEnvironment processingEnv, Type... types) { + if (isEmpty(types)) { + return emptyList(); + } + List typeMirrors = of(types) + .filter(Objects::nonNull) + .map(t -> getTypeMirror(processingEnv, t)) + .filter(Objects::nonNull) + .collect(toList()); + return typeMirrors.isEmpty() ? emptyList() : unmodifiableList(typeMirrors); + } + + /** + * Converts the given Type to a TypeMirror using the provided ProcessingEnvironment. + * + *

If the provided Type is null, this method returns null. Otherwise, it attempts + * to resolve the Type into a TypeElement and then retrieves its corresponding TypeMirror. + * + * @param processingEnv The ProcessingEnvironment used to resolve TypeMirrors. Must not be null. + * @param type The Type to convert to a TypeMirror. May be null. + * @return The resolved TypeMirror if available; otherwise, null. + */ + @Nullable + static TypeMirror getTypeMirror(ProcessingEnvironment processingEnv, Type type) { + TypeElement typeElement = getTypeElement(processingEnv, type); + return typeElement == null ? null : typeElement.asType(); + } + + /** + * Converts an array of Type objects to a list of TypeElement instances using the provided ProcessingEnvironment. + * If the input array is null or empty, returns an empty list. + * + * @param processingEnv The ProcessingEnvironment used to resolve TypeElements. Must not be null. + * @param types The array of Type objects to convert. May contain null elements which will be ignored. + * @return A list of TypeElement instances derived from the given Types. Returns an empty list if the input array is null or empty, + * or if no valid TypeElement instances could be resolved. + */ + @Nonnull + @Immutable + static List getTypeElements(ProcessingEnvironment processingEnv, Type... types) { + if (isEmpty(types)) { + return emptyList(); + } + List typeElements = of(types) + .filter(Objects::nonNull) + .map(t -> getTypeElement(processingEnv, t)) + .filter(Objects::nonNull) + .collect(toList()); + return typeElements.isEmpty() ? emptyList() : unmodifiableList(typeElements); + } + + /** + * Retrieves the TypeElement corresponding to the given Type using the provided ProcessingEnvironment. + * + *

If the provided {@link Type} is null, this method returns null. Otherwise, it attempts + * to resolve the Type into a TypeElement by first obtaining its fully qualified type name + * and then using the ElementUtils from the ProcessingEnvironment. + * + * @param processingEnv The ProcessingEnvironment used to resolve TypeElements. Must not be null. + * @param type The Type to convert to a TypeElement. May be null. + * @return The resolved TypeElement if available; otherwise, null. + */ + @Nullable + static TypeElement getTypeElement(ProcessingEnvironment processingEnv, Type type) { + return type == null ? null : getTypeElement(processingEnv, type.getTypeName()); + } + + /** + * Retrieves the TypeElement corresponding to the given TypeMirror using the provided ProcessingEnvironment. + * + *

If the provided {@link TypeMirror} is null, this method returns null. Otherwise, it attempts + * to resolve the TypeMirror into a TypeElement by first obtaining its fully qualified type name + * and then using the ElementUtils from the ProcessingEnvironment. + * + * @param processingEnv The ProcessingEnvironment used to resolve TypeElements. Must not be null. + * @param type The TypeMirror to convert to a TypeElement. May be null. + * @return The resolved TypeElement if available; otherwise, null. + */ + @Nullable + static TypeElement getTypeElement(ProcessingEnvironment processingEnv, TypeMirror type) { + return type == null ? null : getTypeElement(processingEnv, type.toString()); + } + + /** + * Retrieves the TypeElement corresponding to the given type name using the provided ProcessingEnvironment. + * + *

If the provided {@link ProcessingEnvironment} or typeName is null, this method returns null. + * Otherwise, it uses the ElementUtils from the ProcessingEnvironment to find and return the TypeElement. + * + * @param processingEnv The ProcessingEnvironment used to resolve TypeElements. Must not be null. + * @param typeName The fully qualified class name of the type to search for. May be null. + * @return The resolved TypeElement if available; otherwise, null. + */ + @Nullable + static TypeElement getTypeElement(ProcessingEnvironment processingEnv, CharSequence typeName) { + if (processingEnv == null || typeName == null) { + return null; + } + Elements elements = processingEnv.getElementUtils(); + return elements.getTypeElement(typeName); + } + + /** + * Retrieves the DeclaredType corresponding to the given Type using the provided ProcessingEnvironment. + * + *

If the provided {@link Type} is null, this method returns null. Otherwise, it attempts + * to resolve the Type into a DeclaredType by first obtaining its fully qualified type name + * and then using the ElementUtils from the ProcessingEnvironment to find the corresponding TypeElement. + * + * @param processingEnv The ProcessingEnvironment used to resolve DeclaredTypes. Must not be null. + * @param type The Type to convert to a DeclaredType. May be null. + * @return The resolved DeclaredType if available; otherwise, null. + */ + @Nullable + static DeclaredType getDeclaredType(ProcessingEnvironment processingEnv, Type type) { + return type == null ? null : getDeclaredType(processingEnv, type.getTypeName()); + } + + /** + * Retrieves the DeclaredType corresponding to the given TypeMirror using the provided ProcessingEnvironment. + * + *

If the provided {@link TypeMirror} is null, this method returns null. Otherwise, it attempts + * to resolve the TypeMirror into a DeclaredType by first obtaining its fully qualified type name + * and then using the ElementUtils from the ProcessingEnvironment to find the corresponding TypeElement. + * + * @param processingEnv The ProcessingEnvironment used to resolve DeclaredTypes. Must not be null. + * @param type The TypeMirror to convert to a DeclaredType. May be null. + * @return The resolved DeclaredType if available; otherwise, null. + */ + @Nullable + static DeclaredType getDeclaredType(ProcessingEnvironment processingEnv, TypeMirror type) { + return type == null ? null : getDeclaredType(processingEnv, type.toString()); + } + + /** + * Retrieves the DeclaredType corresponding to the given type name using the provided ProcessingEnvironment. + * + *

If the provided {@link ProcessingEnvironment} or typeName is null, this method returns null. + * Otherwise, it uses the ElementUtils from the ProcessingEnvironment to find and return the TypeElement, + * then converts it to a DeclaredType. + * + * @param processingEnv The ProcessingEnvironment used to resolve DeclaredTypes. Must not be null. + * @param typeName The fully qualified class name of the type to search for. May be null. + * @return The resolved DeclaredType if available; otherwise, null. + */ + @Nullable + static DeclaredType getDeclaredType(ProcessingEnvironment processingEnv, CharSequence typeName) { + return ofDeclaredType(getTypeElement(processingEnv, typeName)); + } + + /** + * Converts the given TypeMirror to its string representation. + * This method is typically used to obtain a readable and fully qualified name of the type, + * including any type parameters if present. + * + * @param type The TypeMirror to convert to a string, may be null. + * @return The string representation of the TypeMirror, or null if the input is null. + */ + @Nullable + static String toString(TypeMirror type) { + return getTypeName(type); + } + + /** + * Gets the fully qualified name of the given TypeMirror, including type parameters if present. + * + * @param type The TypeMirror to get the name from, may be null. + * @return The fully qualified name of the type including type parameters, or null if the input is null. + */ + @Nullable + static String getTypeName(TypeMirror type) { + if (type == null) { + return null; + } + TypeElement element = ofTypeElement(type); + if (element != null) { + List typeParameterElements = element.getTypeParameters(); + int typeParameterElementsSize = typeParameterElements.size(); + if (typeParameterElementsSize > 0) { + List typeMirrors = invokeMethod(type, "getTypeArguments"); + int size = typeMirrors.size(); + if (size > 0) { + StringBuilder typeBuilder = new StringBuilder(element.toString()); + typeBuilder.append(LESS_THAN_CHAR) + .append(typeMirrors) + .append(GREATER_THAN_CHAR); + return typeBuilder.toString(); + } + } + } + return type.toString(); + } + + /** + * Creates a TypeFinder instance for the specified TypeElement with configurable inclusion options. + * + *

This method allows searching for associated types based on the provided criteria: + * + *

    + *
  • {@code includeSelf} - Whether to include the type itself in the result.
  • + *
  • {@code includeHierarchicalTypes} - Whether to include types from the entire hierarchy (e.g., superclasses and interfaces).
  • + *
  • {@code includeSuperclass} - Whether to include direct or hierarchical superclasses based on includeHierarchicalTypes.
  • + *
  • {@code includeInterfaces} - Whether to include direct or hierarchical interfaces based on includeHierarchicalTypes.
  • + *
+ * + * @param typeElement The TypeElement to start the search from. Must not be null. + * @param includeSelf Whether to include the type itself in the result. + * @param includeHierarchicalTypes Whether to include types from the entire hierarchy. + * @param includeSuperclass Whether to include direct or hierarchical superclasses. + * @param includeInterfaces Whether to include direct or hierarchical interfaces. + * @return A TypeFinder instance configured with the given parameters. + * @throws IllegalArgumentException if any parameter is invalid or if assertions fail. + */ + @Nonnull + static TypeFinder typeElementFinder(TypeElement typeElement, boolean includeSelf, + boolean includeHierarchicalTypes, boolean includeSuperclass, boolean includeInterfaces) { + return new TypeFinder(typeElement, TYPE_ELEMENT_GET_SUPERCLASS, TYPE_ELEMENT_GET_INTERFACES, includeSelf, + includeHierarchicalTypes, includeSuperclass, includeInterfaces); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/element/StringAnnotationValueTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/element/StringAnnotationValueTest.java new file mode 100644 index 000000000..df77ef12c --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/element/StringAnnotationValueTest.java @@ -0,0 +1,53 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.element; + + +import io.microsphere.lang.model.util.ResolvableAnnotationValueVisitor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link StringAnnotationValue} Test + * + * @author
Mercy + * @see StringAnnotationValue + * @since 1.0.0 + */ +class StringAnnotationValueTest { + + private StringAnnotationValue value; + + @BeforeEach + void setUp() { + this.value = new StringAnnotationValue("testing"); + } + + @Test + void testGetValue() { + assertEquals("testing", value.getValue()); + } + + @Test + void testAccept() { + ResolvableAnnotationValueVisitor visitor = new ResolvableAnnotationValueVisitor(); + assertEquals("testing", value.accept(visitor, null)); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java new file mode 100644 index 000000000..4dec272bb --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java @@ -0,0 +1,775 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.lang.model.element.StringAnnotationValue; +import io.microsphere.test.annotation.TestAnnotation; +import io.microsphere.test.model.Model; +import io.microsphere.test.service.TestService; +import io.microsphere.test.service.TestServiceImpl; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScans; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.xml.ws.ServiceMode; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Target; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import static io.microsphere.lang.function.Predicates.alwaysFalse; +import static io.microsphere.lang.function.Predicates.alwaysTrue; +import static io.microsphere.lang.model.util.AnnotationUtils.EMPTY_ELEMENT_TYPE_ARRAY; +import static io.microsphere.lang.model.util.AnnotationUtils.findAllAnnotations; +import static io.microsphere.lang.model.util.AnnotationUtils.findAnnotation; +import static io.microsphere.lang.model.util.AnnotationUtils.findAnnotations; +import static io.microsphere.lang.model.util.AnnotationUtils.findMetaAnnotation; +import static io.microsphere.lang.model.util.AnnotationUtils.getAllAnnotations; +import static io.microsphere.lang.model.util.AnnotationUtils.getAnnotation; +import static io.microsphere.lang.model.util.AnnotationUtils.getAnnotations; +import static io.microsphere.lang.model.util.AnnotationUtils.getAttribute; +import static io.microsphere.lang.model.util.AnnotationUtils.getAttributeName; +import static io.microsphere.lang.model.util.AnnotationUtils.getAttributesMap; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementTypes; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValue; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValues; +import static io.microsphere.lang.model.util.AnnotationUtils.getValue; +import static io.microsphere.lang.model.util.AnnotationUtils.isAnnotationPresent; +import static io.microsphere.lang.model.util.AnnotationUtils.matchesAnnotationTypeName; +import static io.microsphere.lang.model.util.AnnotationUtils.matchesAttributeMethod; +import static io.microsphere.lang.model.util.AnnotationUtils.matchesAttributeValue; +import static io.microsphere.lang.model.util.AnnotationUtils.matchesDefaultAttributeValue; +import static io.microsphere.lang.model.util.FieldUtils.findField; +import static io.microsphere.lang.model.util.MethodUtils.findMethod; +import static io.microsphere.lang.model.util.MethodUtils.getAllDeclaredMethods; +import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; +import static io.microsphere.util.ArrayUtils.ofArray; +import static io.microsphere.util.StringUtils.EMPTY_STRING; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The {@link AnnotationUtils} Test + * + * @author Mercy + * @since 1.0.0 + */ +class AnnotationUtilsTest extends UtilTest { + + @Test + void testGetAnnotation() { + asserGetAnnotation(Service.class); + } + + @Test + void testGetAnnotationWithClassName() { + asserGetAnnotation("org.springframework.stereotype.Service"); + } + + @Test + void testGetAnnotationOnNull() { + assertNull(getAnnotation(testTypeElement, NULL_CLASS)); + assertNull(getAnnotation(testTypeElement.asType(), NULL_CLASS)); + assertNull(getAnnotation(NULL_ANNOTATED_CONSTRUCT, NULL_CLASS)); + } + + @Test + void testGetAnnotationWithClassNameOnNull() { + assertNull(getAnnotation(testTypeElement, NULL_STRING)); + assertNull(getAnnotation(testTypeElement.asType(), NULL_STRING)); + assertNull(getAnnotation(NULL_ANNOTATED_CONSTRUCT, NULL_STRING)); + } + + @Test + void testGetAnnotations() { + List annotations = getAnnotations(testTypeElement); + assertEquals(4, annotations.size()); + assertAnnotation(annotations.get(0), Service.class); + assertAnnotation(annotations.get(1), ServiceMode.class); + assertAnnotation(annotations.get(2), ComponentScans.class); + assertAnnotation(annotations.get(3), TestAnnotation.class); + } + + @Test + void testGetAnnotationsOnNull() { + List annotations = getAnnotations(NULL_ANNOTATED_CONSTRUCT); + assertEmptyList(annotations); + } + + @Test + void testGetAnnotationsWithAnnotationClass() { + assertGetAnnotations(Service.class); + assertGetAnnotations(ServiceMode.class); + } + + @Test + void testGetAnnotationsWithAnnotationClassOnNull() { + assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, NULL_CLASS).isEmpty()); + assertTrue(getAnnotations(testTypeElement, NULL_CLASS).isEmpty()); + assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, Service.class).isEmpty()); + } + + @Test + void testGetAnnotationsWithAnnotationClassOnNotFound() { + List annotations = getAnnotations(testTypeElement, Override.class); + assertEquals(0, annotations.size()); + } + + @Test + void testGetAnnotationsWithAnnotationClassName() { + assertGetAnnotations("org.springframework.stereotype.Service"); + assertGetAnnotations("javax.xml.ws.ServiceMode"); + } + + @Test + void testGetAnnotationsWithAnnotationClassNameOnNull() { + assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, NULL_STRING).isEmpty()); + assertTrue(getAnnotations(testTypeElement, NULL_STRING).isEmpty()); + assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, "org.springframework.stereotype.Service").isEmpty()); + } + + @Test + void testGetAllAnnotations() { + List annotations = getAllAnnotations(testTypeElement); + assertEquals(5, annotations.size()); + + annotations = getAllAnnotations(testTypeMirror); + assertEquals(5, annotations.size()); + } + + @Test + void testGetAllAnnotationsOnNull() { + assertEmptyList(getAllAnnotations(NULL_ELEMENT)); + assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR)); + } + + @Test + void testGetAllAnnotationsWithAnnotationClass() { + List annotations = getAllAnnotations(testTypeElement, Override.class); + assertEquals(0, annotations.size()); + + annotations = getAllAnnotations(testTypeMirror, Override.class); + assertEquals(0, annotations.size()); + + annotations = getAllAnnotations(testTypeElement, Service.class); + assertEquals(1, annotations.size()); + + annotations = getAllAnnotations(testTypeMirror, Service.class); + assertEquals(1, annotations.size()); + + annotations = getAllAnnotations(processingEnv, TestServiceImpl.class); + assertEquals(5, annotations.size()); + } + + @Test + void testGetAllAnnotationsWithAnnotationClassOnNull() { + assertEmptyList(getAllAnnotations(NULL_ELEMENT, NULL_CLASS)); + assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR, NULL_CLASS)); + assertEmptyList(getAllAnnotations(NULL_PROCESSING_ENVIRONMENT, NULL_CLASS)); + + assertEmptyList(getAllAnnotations(NULL_ELEMENT, Service.class)); + assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR, Service.class)); + assertEmptyList(getAllAnnotations(NULL_PROCESSING_ENVIRONMENT, Service.class)); + + assertEmptyList(getAllAnnotations(testTypeElement, NULL_CLASS)); + assertEmptyList(getAllAnnotations(testTypeMirror, NULL_CLASS)); + assertEmptyList(getAllAnnotations(processingEnv, NULL_CLASS)); + } + + @Test + void testGetAllAnnotationsWithAnnotationClassName() { + List annotations = getAllAnnotations(testTypeElement, "java.lang.Override"); + assertEquals(0, annotations.size()); + + annotations = getAllAnnotations(testTypeMirror, "org.springframework.stereotype.Service"); + assertEquals(1, annotations.size()); + } + + @Test + void testGetAllAnnotationsWithAnnotationClassNameOnNull() { + assertEmptyList(getAllAnnotations(NULL_ELEMENT, NULL_STRING)); + assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR, NULL_STRING)); + + assertTrue(getAllAnnotations(NULL_ELEMENT, "org.springframework.stereotype.Service").isEmpty()); + assertTrue(getAllAnnotations(NULL_TYPE_MIRROR, "org.springframework.stereotype.Service").isEmpty()); + + assertEmptyList(getAllAnnotations(testTypeElement, NULL_STRING)); + assertEmptyList(getAllAnnotations(testTypeMirror, NULL_STRING)); + } + + @Test + void testFindAnnotation() { + assertFindAnnotation(Service.class); + assertFindAnnotation(Path.class); + } + + @Test + void testFindAnnotationOnNotFound() { + assertNull(findAnnotation(testTypeMirror, Target.class)); + assertNull(findAnnotation(testTypeElement, Target.class)); + assertNull(findAnnotation(testTypeMirror, Override.class)); + assertNull(findAnnotation(testTypeElement, Override.class)); + } + + @Test + void testFindAnnotationOnNull() { + assertNull(findAnnotation(NULL_ELEMENT, NULL_CLASS)); + assertNull(findAnnotation(NULL_TYPE_MIRROR, NULL_CLASS)); + assertNull(findAnnotation(testTypeMirror, NULL_CLASS)); + assertNull(findAnnotation(testTypeElement, NULL_CLASS)); + + assertNull(findAnnotation(NULL_ELEMENT, NULL_STRING)); + assertNull(findAnnotation(NULL_TYPE_MIRROR, NULL_STRING)); + assertNull(findAnnotation(testTypeMirror, NULL_STRING)); + assertNull(findAnnotation(testTypeElement, NULL_STRING)); + } + + @Test + void testFindMetaAnnotationWithAnnotationClass() { + getAllDeclaredMethods(getTypeElement(TestService.class)).forEach(method -> { + assertFindMetaAnnotation(method, HttpMethod.class); + }); + } + + @Test + void testFindMetaAnnotationWithAnnotationClassOnNotFound() { + assertNull(findMetaAnnotation(testTypeElement, Service.class)); + } + + @Test + void testFindMetaAnnotationWithAnnotationClassNameOnNotFound() { + assertNull(findMetaAnnotation(testTypeElement, "org.springframework.stereotype.Service")); + } + + @Test + void testFindMetaAnnotationWithAnnotationClassOnNull() { + assertNull(findMetaAnnotation(NULL_ELEMENT, NULL_CLASS)); + assertNull(findMetaAnnotation(NULL_ELEMENT, Service.class)); + assertNull(findMetaAnnotation(testTypeElement, NULL_CLASS)); + } + + @Test + void testFindMetaAnnotationWithAnnotationClassName() { + getAllDeclaredMethods(getTypeElement(TestService.class)).forEach(method -> { + assertFindMetaAnnotation(method, "javax.ws.rs.HttpMethod"); + }); + } + + @Test + void testFindMetaAnnotationWithAnnotationClassNameOnNull() { + assertNull(findMetaAnnotation(NULL_ELEMENT, NULL_STRING)); + assertNull(findMetaAnnotation(NULL_ELEMENT, "test")); + assertNull(findMetaAnnotation(testTypeElement, NULL_STRING)); + } + + @Test + void testFindAllAnnotationsWithTypeMirror() { + List annotations = findAllAnnotations(testTypeMirror, alwaysTrue()); + assertEquals(5, annotations.size()); + + annotations = findAllAnnotations(testTypeMirror, alwaysFalse()); + assertEmptyList(annotations); + } + + @Test + void testFindAllAnnotationsWithTypeElement() { + List annotations = findAllAnnotations(testTypeElement, alwaysTrue()); + assertEquals(5, annotations.size()); + + annotations = findAllAnnotations(testTypeElement, alwaysFalse()); + assertEmptyList(annotations); + } + + @Test + void testFindAllAnnotationsWithMethod() { + ExecutableElement method = findMethod(testTypeElement, "echo", String.class); + + List annotations = findAllAnnotations(method, alwaysTrue()); + assertEquals(1, annotations.size()); + assertAnnotation(annotations.get(0), Cacheable.class); + + method = findMethod(getTypeElement(TestService.class), "echo", String.class); + + annotations = findAllAnnotations(method); + assertEquals(1, annotations.size()); + assertAnnotation(annotations.get(0), GET.class); + } + + @Test + void testFindAllAnnotationsWithMethodParameters() { + ExecutableElement method = findMethod(getTypeElement(TestService.class), "echo", String.class); + List parameters = method.getParameters(); + assertEquals(1, parameters.size()); + + List annotations = findAllAnnotations(parameters.get(0), alwaysTrue()); + assertEquals(2, annotations.size()); + assertAnnotation(annotations.get(0), PathParam.class); + assertAnnotation(annotations.get(1), DefaultValue.class); + + method = findMethod(getTypeElement(TestService.class), "model", Model.class); + parameters = method.getParameters(); + assertEquals(1, parameters.size()); + + annotations = findAllAnnotations(parameters.get(0)); + assertEquals(1, annotations.size()); + assertAnnotation(annotations.get(0), PathParam.class); + } + + @Test + void testFindAllAnnotationsWithField() { + VariableElement field = findField(testTypeElement, "context"); + + List annotations = findAllAnnotations(field, alwaysTrue()); + assertEquals(1, annotations.size()); + assertAnnotation(annotations.get(0), Autowired.class); + + field = findField(testTypeElement, "environment"); + annotations = findAllAnnotations(field, alwaysTrue()); + assertEmptyList(annotations); + } + + @Test + void testFindAllAnnotationsWithTypeMirrorOnNull() { + assertEmptyList(findAllAnnotations(NULL_TYPE_MIRROR, alwaysTrue())); + assertEmptyList(findAllAnnotations(NULL_TYPE_MIRROR, alwaysFalse())); + } + + @Test + void testFindAllAnnotationsWithTypeElementOnNull() { + assertEmptyList(findAllAnnotations(NULL_TYPE_ELEMENT, alwaysTrue())); + assertEmptyList(findAllAnnotations(NULL_TYPE_ELEMENT, alwaysFalse())); + } + + @Test + void testFindAllAnnotationsWithElementOnNull() { + assertEmptyList(findAllAnnotations(NULL_ELEMENT, alwaysTrue())); + assertEmptyList(findAllAnnotations(NULL_ELEMENT, alwaysFalse())); + } + + @Test + void testFindAllAnnotationsOnNull() { + assertEmptyList(findAllAnnotations(NULL_PROCESSING_ENVIRONMENT, Service.class, alwaysTrue())); + assertEmptyList(findAllAnnotations(NULL_PROCESSING_ENVIRONMENT, Service.class, alwaysTrue())); + assertEmptyList(findAllAnnotations(NULL_PROCESSING_ENVIRONMENT, "org.springframework.stereotype.Service", alwaysFalse())); + assertEmptyList(findAllAnnotations(NULL_PROCESSING_ENVIRONMENT, "org.springframework.stereotype.Service", alwaysFalse())); + assertEmptyList(findAllAnnotations(processingEnv, NULL_TYPE, alwaysTrue())); + assertEmptyList(findAllAnnotations(processingEnv, NULL_TYPE, alwaysFalse())); + assertEmptyList(findAllAnnotations(processingEnv, NULL_STRING, alwaysTrue())); + assertEmptyList(findAllAnnotations(processingEnv, NULL_STRING, alwaysFalse())); + } + + @Test + void testMatchesAnnotationClass() { + AnnotationMirror annotation = findAnnotation(testTypeElement, Service.class); + assertTrue(AnnotationUtils.matchesAnnotationType(annotation, Service.class)); + } + + @Test + void testMatchesAnnotationClassOnNull() { + assertFalse(AnnotationUtils.matchesAnnotationType(NULL_ANNOTATION_MIRROR, Service.class)); + assertFalse(AnnotationUtils.matchesAnnotationType(findAnnotation(testTypeElement, Service.class), NULL_CLASS)); + } + + @Test + void testMatchesAnnotationTypeName() { + AnnotationMirror annotation = findAnnotation(testTypeElement, "org.springframework.stereotype.Service"); + assertTrue(matchesAnnotationTypeName(annotation, "org.springframework.stereotype.Service")); + } + + @Test + void testMatchesAnnotationTypeNameOnNull() { + assertFalse(matchesAnnotationTypeName(NULL_ANNOTATION_MIRROR, "org.springframework.stereotype.Service")); + assertFalse(matchesAnnotationTypeName(findAnnotation(testTypeElement, "org.springframework.stereotype.Service"), NULL_STRING)); + } + + @Test + void testGetAttribute() { + assertEquals("testService", getAttribute(findAnnotation(testTypeElement, Service.class), "value")); + assertEquals("testService", getAttribute(findAnnotation(testTypeElement, Service.class), "value", false)); + assertEquals("/echo", getAttribute(findAnnotation(testTypeElement, Path.class), "value")); + + assertNull(getAttribute(findAnnotation(testTypeElement, Path.class), NULL_STRING)); + assertNull(getAttribute(findAnnotation(testTypeElement, NULL_CLASS), NULL_STRING)); + + ExecutableElement echoMethod = findMethod(testTypeElement, "echo", String.class); + AnnotationMirror cacheableAnnotation = findAnnotation(echoMethod, Cacheable.class); + String[] cacheNames = getAttribute(cacheableAnnotation, "cacheNames"); + assertArrayEquals(ofArray("cache-1", "cache-2"), cacheNames); + + String key = getAttribute(cacheableAnnotation, "key"); + assertEquals(EMPTY_STRING, key); + + DeclaredType cacheableAnnotationType = cacheableAnnotation.getAnnotationType(); + AnnotationMirror targetAnnotation = findAnnotation(cacheableAnnotationType, Target.class); + ElementType[] elementTypes = getAttribute(targetAnnotation, "value"); + assertArrayEquals(ofArray(TYPE, METHOD), elementTypes); + + } + + @Test + void testGetValue() { + AnnotationMirror pathAnnotation = getAnnotation(getTypeElement(TestService.class), Path.class); + assertEquals("/echo", getValue(pathAnnotation)); + } + + @Test + void testIsAnnotationPresentOnAnnotationClass() { + assertTrue(isAnnotationPresent(testTypeElement, Service.class)); + assertTrue(isAnnotationPresent(testTypeElement, Component.class)); + assertTrue(isAnnotationPresent(testTypeElement, ServiceMode.class)); + assertTrue(isAnnotationPresent(testTypeElement, Inherited.class)); + assertTrue(isAnnotationPresent(testTypeElement, Documented.class)); + } + + @Test + void testIsAnnotationPresentOnAnnotationClassOnNull() { + assertFalse(isAnnotationPresent(NULL_ELEMENT, Service.class)); + assertFalse(isAnnotationPresent(testTypeElement, NULL_CLASS)); + assertFalse(isAnnotationPresent(testTypeElement, Override.class)); + } + + @Test + void testIsAnnotationPresentOnAnnotationClassName() { + assertTrue(isAnnotationPresent(testTypeElement, "org.springframework.stereotype.Service")); + assertTrue(isAnnotationPresent(testTypeElement, "org.springframework.stereotype.Component")); + assertTrue(isAnnotationPresent(testTypeElement, "javax.xml.ws.ServiceMode")); + assertTrue(isAnnotationPresent(testTypeElement, "java.lang.annotation.Inherited")); + assertTrue(isAnnotationPresent(testTypeElement, "java.lang.annotation.Documented")); + } + + @Test + void testIsAnnotationPresentOnAnnotationClassNameOnNull() { + assertFalse(isAnnotationPresent(NULL_ELEMENT, "org.springframework.stereotype.Service")); + assertFalse(isAnnotationPresent(testTypeElement, NULL_STRING)); + assertFalse(isAnnotationPresent(testTypeElement, "java.lang.Override")); + + } + + @Test + void testFindAnnotations() { + List annotations = findAnnotations(testTypeElement); + assertEquals(4, annotations.size()); + assertAnnotation(annotations.get(0), Service.class); + assertAnnotation(annotations.get(1), ServiceMode.class); + assertAnnotation(annotations.get(2), ComponentScans.class); + assertAnnotation(annotations.get(3), TestAnnotation.class); + + annotations = findAnnotations(testTypeElement, alwaysTrue()); + assertEquals(4, annotations.size()); + assertAnnotation(annotations.get(0), Service.class); + assertAnnotation(annotations.get(1), ServiceMode.class); + assertAnnotation(annotations.get(2), ComponentScans.class); + assertAnnotation(annotations.get(3), TestAnnotation.class); + + annotations = findAnnotations(testTypeElement, alwaysFalse()); + assertEmptyList(annotations); + } + + @Test + void testFindAnnotationsOnNotFound() { + assertEmptyList(findAnnotations(getTypeElement(Serializable.class))); + } + + @Test + void testFindAnnotationsOnNull() { + assertEmptyList(findAnnotations(NULL_ELEMENT)); + } + + @Test + void testGetAttributeName() { + Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + for (Entry entry : elementValues.entrySet()) { + ExecutableElement attributeMethod = entry.getKey(); + assertEquals(attributeMethod.getSimpleName().toString(), getAttributeName(attributeMethod)); + } + } + + @Test + void testMatchesAttributeMethod() { + Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + for (Entry entry : elementValues.entrySet()) { + ExecutableElement attributeMethod = entry.getKey(); + assertTrue(matchesAttributeMethod(attributeMethod, getAttributeName(attributeMethod))); + } + } + + @Test + void testMatchesAttributeMethodOnNull() { + assertFalse(matchesAttributeMethod(null, null)); + + Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + for (Entry entry : elementValues.entrySet()) { + ExecutableElement attributeMethod = entry.getKey(); + assertFalse(matchesAttributeMethod(attributeMethod, null)); + } + } + + @Test + void testMatchesAttributeValue() { + Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + for (Entry entry : elementValues.entrySet()) { + AnnotationValue annotationValue = entry.getValue(); + assertTrue(matchesAttributeValue(annotationValue, annotationValue)); + assertTrue(matchesAttributeValue(annotationValue, annotationValue.getValue())); + } + + assertTrue(matchesAttributeValue(new StringAnnotationValue(""), new StringAnnotationValue(""))); + } + + @Test + void testMatchesAttributeValueOnNull() { + assertTrue(matchesAttributeValue(null, null)); + assertFalse(matchesAttributeValue(null, (Object) null)); + + Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + for (Entry entry : elementValues.entrySet()) { + AnnotationValue annotationValue = entry.getValue(); + assertFalse(matchesAttributeValue(annotationValue, null)); + assertFalse(matchesAttributeValue(annotationValue, (Object) null)); + } + } + + @Test + void testMatchesDefaultAttributeValue() { + Map elementValues = getElementValues(testTypeElement, ServiceMode.class); + for (Entry entry : elementValues.entrySet()) { + ExecutableElement attributeMethod = entry.getKey(); + assertTrue(matchesDefaultAttributeValue(attributeMethod, attributeMethod.getDefaultValue())); + } + } + + @Test + void testGetElementValue() { + Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + for (Entry entry : elementValues.entrySet()) { + ExecutableElement attributeMethod = entry.getKey(); + String attributeName = getAttributeName(attributeMethod); + assertEquals(entry, getElementValue(elementValues, attributeName)); + } + + assertNull(getElementValue(elementValues, "unknown")); + } + + @Test + void testGetElementValueOnEmptyElementValues() { + AnnotationMirror annotation = findAnnotation(testTypeElement, ServiceMode.class); + Map elementValues = annotation.getElementValues(); + assertNull(getElementValue(elementValues, "value")); + } + + @Test + void testGetElementValueOnNull() { + assertNull(getElementValue(null, "value")); + } + + @Test + void testGetElementValuesMapOnAnnotatedClass() { + Map attributesMap = getAttributesMap(testTypeElement, Service.class); + assertEquals(1, attributesMap.size()); + assertEquals("testService", attributesMap.get("value")); + } + + @Test + void testGetElementValuesMapOnAnnotatedMethod() { + ExecutableElement method = findMethod(testTypeElement, "echo", String.class); + Map attributesMap = getAttributesMap(method, Cacheable.class); + assertEquals(9, attributesMap.size()); + assertArrayEquals(ofArray("cache-1", "cache-2"), (String[]) attributesMap.get("cacheNames")); + } + + @Test + void testGetElementValuesMapOnRepeatableAnnotation() { + Map attributesMap = getAttributesMap(testTypeElement, ComponentScans.class); + assertEquals(1, attributesMap.size()); + + ComponentScans componentScans = testClass.getAnnotation(ComponentScans.class); + ComponentScan[] componentScanArray = (ComponentScan[]) attributesMap.get("value"); + assertEquals(2, componentScanArray.length); + assertArrayEquals(componentScanArray, componentScans.value()); + } + + @Test + void testGetElementValuesMapOnNull() { + Map attributesMap = getAttributesMap(null, null); + assertSame(emptyMap(), attributesMap); + + attributesMap = getAttributesMap(testTypeElement, null); + assertSame(emptyMap(), attributesMap); + + attributesMap = getAttributesMap(null); + assertSame(emptyMap(), attributesMap); + } + + @Test + void testGetElementValuesOnAnnotatedClass() { + Map elementValues = getElementValues(testTypeElement, Service.class); + assertServiceAttributes(elementValues); + + elementValues = getElementValues(testTypeElement, Service.class, false); + assertServiceAttributes(elementValues); + } + + @Test + void testGetElementValuesOnAnnotatedMethod() { + ExecutableElement method = findMethod(testTypeElement, "echo", String.class); + Map elementValues = getElementValues(method, Cacheable.class, false); + assertEquals(1, elementValues.size()); + assertAttributeEntry(elementValues, "cacheNames", ofArray("cache-1", "cache-2")); + + + elementValues = getElementValues(method, Cacheable.class, true); + assertEquals(9, elementValues.size()); + assertAttributeEntry(elementValues, "value", EMPTY_STRING_ARRAY); + assertAttributeEntry(elementValues, "cacheNames", ofArray("cache-1", "cache-2")); + assertAttributeEntry(elementValues, "key", EMPTY_STRING); + assertAttributeEntry(elementValues, "keyGenerator", EMPTY_STRING); + assertAttributeEntry(elementValues, "cacheManager", EMPTY_STRING); + assertAttributeEntry(elementValues, "cacheResolver", EMPTY_STRING); + assertAttributeEntry(elementValues, "condition", EMPTY_STRING); + assertAttributeEntry(elementValues, "unless", EMPTY_STRING); + assertAttributeEntry(elementValues, "sync", false); + } + + @Test + void testGetElementValuesOnNull() { + Map elementValues = getElementValues(null); + assertSame(emptyMap(), elementValues); + } + + @Test + void testGetElementTypes() { + assertElementTypes(Service.class, TYPE); + assertElementTypes(ServiceMode.class, TYPE); + assertElementTypes(ComponentScans.class, TYPE); + assertElementTypes(TestAnnotation.class, TYPE); + } + + void assertElementTypes(Class annotationClass, ElementType... expectedElementTypes) { + AnnotationMirror annotationMirror = findAnnotation(this.testTypeElement, annotationClass); + assertArrayEquals(expectedElementTypes, getElementTypes(annotationMirror)); + } + + @Test + void testGetElementTypesOnNull() { + assertSame(EMPTY_ELEMENT_TYPE_ARRAY, getElementTypes((AnnotationMirror) null)); + assertSame(EMPTY_ELEMENT_TYPE_ARRAY, getElementTypes((DeclaredType) null)); + } + + void assertServiceAttributes(Map attributes) { + assertEquals(1, attributes.size()); + assertAttributeEntry(attributes, "value", "testService"); + } + + void assertAttributeEntry(Map attributes, String attributeName, Object attributeValue) { + for (Entry entry : attributes.entrySet()) { + ExecutableElement attributeMethod = entry.getKey(); + if (matchesAttributeMethod(attributeMethod, attributeName)) { + assertAttributeEntry(entry, attributeName, attributeValue); + break; + } + } + } + + void assertAttributeEntry(Entry attributeEntry, String attributeName, Object attributeValue) { + ExecutableElement attributeMethod = attributeEntry.getKey(); + AnnotationValue annotationValue = attributeEntry.getValue(); + assertEquals(attributeName, getAttributeName(attributeMethod)); + Object value = getAttribute(attributeEntry); + Class attributeValueClass = value.getClass(); + if (attributeValueClass.isArray()) { + Class componentType = attributeValueClass.getComponentType(); + if (String.class.equals(componentType)) { + assertArrayEquals((String[]) attributeValue, (String[]) value); + } + } else { + assertEquals(attributeValue, value); + } + } + + private void assertFindMetaAnnotation(Element element, Class annotationClass) { + assertAnnotation(findMetaAnnotation(element, annotationClass), annotationClass); + } + + private void assertFindMetaAnnotation(Element element, String annotationClassName) { + assertAnnotation(findMetaAnnotation(element, annotationClassName), annotationClassName); + } + + private void assertFindAnnotation(Class annotationClass) { + assertAnnotation(findAnnotation(testTypeMirror, annotationClass), annotationClass); + assertAnnotation(findAnnotation(testTypeElement, annotationClass), annotationClass); + assertAnnotation(findAnnotation(testTypeMirror, annotationClass.getName()), annotationClass); + assertAnnotation(findAnnotation(testTypeElement, annotationClass.getName()), annotationClass); + } + + private void asserGetAnnotation(Class annotationClass) { + AnnotationMirror annotation = getAnnotation(testTypeElement, annotationClass); + assertAnnotation(annotation, annotationClass); + } + + private void asserGetAnnotation(String annotationClassName) { + AnnotationMirror annotation = getAnnotation(testTypeElement, annotationClassName); + assertAnnotation(annotation, annotationClassName); + } + + private void assertGetAnnotations(Class annotationClass) { + List annotations = getAnnotations(testTypeElement, annotationClass); + assertEquals(1, annotations.size()); + assertAnnotation(annotations.get(0), annotationClass); + } + + private void assertGetAnnotations(String annotationClassName) { + List annotations = getAnnotations(testTypeElement, annotationClassName); + assertEquals(1, annotations.size()); + assertAnnotation(annotations.get(0), annotationClassName); + } + + private void assertAnnotation(AnnotationMirror annotation, Class annotationClass) { + assertTrue(AnnotationUtils.matchesAnnotationType(annotation, annotationClass)); + assertAnnotation(annotation, annotationClass.getName()); + } + + private void assertAnnotation(AnnotationMirror annotation, String annotationClassName) { + assertEquals(annotation.getAnnotationType().toString(), annotationClassName); + } +} diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ClassUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ClassUtilsTest.java new file mode 100644 index 000000000..5f6708723 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ClassUtilsTest.java @@ -0,0 +1,51 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.ComponentScan; + +import static io.microsphere.lang.model.util.ClassUtils.getClassName; +import static io.microsphere.lang.model.util.ClassUtils.loadClass; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link ClassUtils} Test + * + * @author Mercy + * @see ClassUtils + * @since 1.0.0 + */ +class ClassUtilsTest extends UtilTest { + + @Test + void testGetClassName() { + assertEquals(this.testClassName, getClassName(this.testTypeMirror)); + } + + @Test + void testLoadClassOnTypeMirror() { + assertSame(this.testClass, loadClass(this.testTypeMirror)); + } + + @Test + void testLoadClass() { + assertSame(ComponentScan.Filter.class, loadClass("org.springframework.context.annotation.ComponentScan.Filter")); + } +} diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ConstructorUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ConstructorUtilsTest.java new file mode 100644 index 000000000..8fd8e5eb5 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ConstructorUtilsTest.java @@ -0,0 +1,150 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + + +import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; +import org.junit.jupiter.api.Test; +import org.springframework.core.env.Environment; + +import javax.lang.model.element.ExecutableElement; +import java.io.Serializable; +import java.util.List; + +import static io.microsphere.lang.function.Predicates.alwaysFalse; +import static io.microsphere.lang.function.Predicates.alwaysTrue; +import static io.microsphere.lang.model.util.ConstructorUtils.findConstructor; +import static io.microsphere.lang.model.util.ConstructorUtils.findDeclaredConstructors; +import static io.microsphere.lang.model.util.ConstructorUtils.getDeclaredConstructors; +import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypes; +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link ConstructorUtils} Test + * + * @author Mercy + * @see ConstructorUtils + * @since 1.0.0 + */ +class ConstructorUtilsTest extends UtilTest { + + @Test + void testGetDeclaredConstructors() { + List constructors = getDeclaredConstructors(this.testTypeElement); + assertTestServiceImplConstructors(constructors); + + constructors = getDeclaredConstructors(this.testDeclaredType); + assertTestServiceImplConstructors(constructors); + } + + @Test + void testGetDeclaredConstructorsOnNull() { + assertSame(emptyList(), getDeclaredConstructors(NULL_TYPE_ELEMENT)); + assertSame(emptyList(), getDeclaredConstructors(NULL_TYPE_MIRROR)); + } + + @Test + void testFindConstructor() { + assertTestServiceImpl1stConstructor(ConstructorUtils.findConstructor(this.testTypeElement)); + assertTestServiceImpl1stConstructor(findConstructor(this.testDeclaredType)); + + assertTestServiceImpl2ndConstructor(ConstructorUtils.findConstructor(this.testTypeElement, Environment.class)); + assertTestServiceImpl2ndConstructor(findConstructor(this.testDeclaredType, Environment.class)); + } + + @Test + void testFindConstructorOnNull() { + assertNull(ConstructorUtils.findConstructor(NULL_TYPE_ELEMENT)); + assertNull(findConstructor(NULL_TYPE_MIRROR)); + + assertNull(ConstructorUtils.findConstructor(this.testTypeElement, null)); + assertNull(findConstructor(this.testDeclaredType, null)); + } + + @Test + void testFindConstructorOnMismatch() { + assertNull(ConstructorUtils.findConstructor(NULL_TYPE_ELEMENT, Object.class)); + assertNull(findConstructor(NULL_TYPE_MIRROR, Object.class)); + + assertNull(ConstructorUtils.findConstructor(NULL_TYPE_ELEMENT, Object.class, String.class)); + assertNull(findConstructor(NULL_TYPE_MIRROR, Object.class, String.class)); + + assertNull(ConstructorUtils.findConstructor(NULL_TYPE_ELEMENT, Object.class, String.class, Integer.class)); + assertNull(findConstructor(NULL_TYPE_MIRROR, Object.class, String.class, Integer.class)); + } + + @Test + void testFindConstructors() { + List constructors = findDeclaredConstructors(this.testTypeElement); + assertTestServiceImplConstructors(constructors); + + constructors = findDeclaredConstructors(this.testDeclaredType); + assertTestServiceImplConstructors(constructors); + + constructors = findDeclaredConstructors(this.testTypeElement, alwaysTrue()); + assertTestServiceImplConstructors(constructors); + + constructors = findDeclaredConstructors(this.testDeclaredType, alwaysTrue()); + assertTestServiceImplConstructors(constructors); + } + + @Test + void testFindConstructorsOnNull() { + assertSame(emptyList(), findDeclaredConstructors(NULL_TYPE_ELEMENT)); + assertSame(emptyList(), findDeclaredConstructors(NULL_TYPE_MIRROR)); + + assertSame(emptyList(), findDeclaredConstructors(this.testTypeElement, null)); + assertSame(emptyList(), findDeclaredConstructors(this.testDeclaredType, null)); + } + + @Test + void testFindConstructorsOnMismatch() { + assertSame(emptyList(), findDeclaredConstructors(this.testTypeElement, alwaysFalse())); + assertSame(emptyList(), findDeclaredConstructors(this.testDeclaredType, alwaysFalse())); + } + + @Test + void testFindConstructorsOnNotFound() { + assertSame(emptyList(), findDeclaredConstructors(getTypeElement(Serializable.class))); + assertSame(emptyList(), findDeclaredConstructors(getDeclaredType(Serializable.class))); + + assertSame(emptyList(), findDeclaredConstructors(getTypeElement(List.class))); + assertSame(emptyList(), findDeclaredConstructors(getDeclaredType(List.class))); + } + + void assertTestServiceImplConstructors(List constructors) { + assertEquals(2, constructors.size()); + assertTestServiceImpl1stConstructor(constructors.get(0)); + assertTestServiceImpl2ndConstructor(constructors.get(1)); + } + + void assertTestServiceImpl1stConstructor(ExecutableElement constructor) { + assertEquals(this.testTypeElement, constructor.getEnclosingElement()); + assertEquals(emptyList(), constructor.getParameters()); + } + + void assertTestServiceImpl2ndConstructor(ExecutableElement constructor) { + assertEquals(this.testTypeElement, constructor.getEnclosingElement()); + assertEquals(1, constructor.getParameters().size()); + assertTrue(matchParameterTypes(constructor, Environment.class)); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java new file mode 100644 index 000000000..6fa9d8130 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java @@ -0,0 +1,351 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import java.lang.annotation.ElementType; +import java.lang.reflect.Method; +import java.util.List; + +import static io.microsphere.collection.ListUtils.ofList; +import static io.microsphere.lang.function.Predicates.alwaysFalse; +import static io.microsphere.lang.function.Predicates.alwaysTrue; +import static io.microsphere.lang.model.util.ElementUtils.filterElements; +import static io.microsphere.lang.model.util.ElementUtils.hasModifiers; +import static io.microsphere.lang.model.util.ElementUtils.isClass; +import static io.microsphere.lang.model.util.ElementUtils.isDeclaredType; +import static io.microsphere.lang.model.util.ElementUtils.isExecutable; +import static io.microsphere.lang.model.util.ElementUtils.isField; +import static io.microsphere.lang.model.util.ElementUtils.isInitializer; +import static io.microsphere.lang.model.util.ElementUtils.isInterface; +import static io.microsphere.lang.model.util.ElementUtils.isMember; +import static io.microsphere.lang.model.util.ElementUtils.isPublicNonStatic; +import static io.microsphere.lang.model.util.ElementUtils.isVariable; +import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypeNames; +import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypes; +import static io.microsphere.lang.model.util.ElementUtils.matchesElementKind; +import static io.microsphere.lang.model.util.ElementUtils.matchesElementType; +import static io.microsphere.lang.model.util.ElementUtils.toElementKind; +import static io.microsphere.lang.model.util.MemberUtils.getAllDeclaredMembers; +import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; +import static io.microsphere.lang.model.util.MethodUtils.findMethod; +import static java.lang.annotation.ElementType.PACKAGE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.ElementType.values; +import static java.util.Collections.emptyList; +import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE; +import static javax.lang.model.element.ElementKind.CLASS; +import static javax.lang.model.element.ElementKind.CONSTRUCTOR; +import static javax.lang.model.element.ElementKind.ENUM; +import static javax.lang.model.element.ElementKind.ENUM_CONSTANT; +import static javax.lang.model.element.ElementKind.EXCEPTION_PARAMETER; +import static javax.lang.model.element.ElementKind.FIELD; +import static javax.lang.model.element.ElementKind.INSTANCE_INIT; +import static javax.lang.model.element.ElementKind.INTERFACE; +import static javax.lang.model.element.ElementKind.LOCAL_VARIABLE; +import static javax.lang.model.element.ElementKind.METHOD; +import static javax.lang.model.element.ElementKind.OTHER; +import static javax.lang.model.element.ElementKind.PARAMETER; +import static javax.lang.model.element.ElementKind.RESOURCE_VARIABLE; +import static javax.lang.model.element.ElementKind.STATIC_INIT; +import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.util.ElementFilter.fieldsIn; +import static javax.lang.model.util.ElementFilter.methodsIn; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link ElementUtils} Test + * + * @author Mercy + * @see ElementUtils + * @since 1.0.0 + */ +class ElementUtilsTest extends UtilTest { + + private ExecutableElement echoMethod; + + @Override + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + this.echoMethod = findMethod(testTypeElement, "echo", "java.lang.String"); + } + + @Test + void testMatchesElementTypeElementKind() { + assertTrue(matchesElementKind(echoMethod, METHOD)); + assertFalse(matchesElementKind(echoMethod, FIELD)); + } + + @Test + void testMatchesElementTypeElementKindOnNull() { + assertFalse(matchesElementKind(NULL_ELEMENT, FIELD)); + assertFalse(matchesElementKind(echoMethod, NULL_ELEMENT_KIND)); + } + + @Test + void testIsPublicNonStatic() { + methodsIn(getDeclaredMembers(testTypeElement)).forEach(method -> assertTrue(isPublicNonStatic(method))); + + // Integer#valueOf(String) is a public static method + assertFalse(isPublicNonStatic(findMethod(getTypeElement(Integer.class), "valueOf", String.class))); + } + + @Test + void testIsPublicNonStaticOnNull() { + assertFalse(isPublicNonStatic(NULL_ELEMENT)); + } + + @Test + void testHasModifiers() { + List members = getAllDeclaredMembers(testTypeElement.asType()); + List fields = fieldsIn(members); + assertTrue(hasModifiers(fields.get(0), PRIVATE)); + } + + @Test + void testHasModifiersOnNull() { + assertFalse(hasModifiers(NULL_ELEMENT)); + assertFalse(hasModifiers(testTypeElement, null)); + } + + @Test + void testIsClass() { + assertTrue(isClass(CLASS)); + assertTrue(isClass(ENUM)); + assertFalse(isClass(INTERFACE)); + } + + @Test + void testIsClassOnNull() { + assertFalse(isClass(null)); + } + + @Test + void testIsInterface() { + assertTrue(isInterface(INTERFACE)); + assertTrue(isInterface(ANNOTATION_TYPE)); + assertFalse(isInterface(CLASS)); + } + + @Test + void testIsInterfaceOnNull() { + assertFalse(isInterface(null)); + } + + @Test + void testIsDeclaredType() { + assertTrue(isDeclaredType(CLASS)); + assertTrue(isDeclaredType(ENUM)); + assertTrue(isDeclaredType(INTERFACE)); + assertTrue(isDeclaredType(ANNOTATION_TYPE)); + assertFalse(isDeclaredType(LOCAL_VARIABLE)); + } + + @Test + void testIsDeclaredTypeOnNull() { + assertFalse(isDeclaredType(null)); + } + + @Test + void testIsField() { + assertTrue(isField(FIELD)); + assertTrue(isField(ENUM_CONSTANT)); + assertFalse(isField(LOCAL_VARIABLE)); + } + + @Test + void testIsFieldOnNull() { + assertFalse(isField(null)); + } + + @Test + void testIsExecutable() { + assertTrue(isExecutable(METHOD)); + assertTrue(isExecutable(CONSTRUCTOR)); + assertTrue(isExecutable(STATIC_INIT)); + assertTrue(isExecutable(INSTANCE_INIT)); + assertFalse(isExecutable(CLASS)); + } + + @Test + void testIsExecutableOnNull() { + assertFalse(isExecutable(null)); + } + + @Test + void testIsMember() { + assertTrue(isMember(METHOD)); + assertTrue(isMember(CONSTRUCTOR)); + assertTrue(isMember(STATIC_INIT)); + assertTrue(isMember(INSTANCE_INIT)); + assertTrue(isMember(FIELD)); + assertTrue(isMember(ENUM_CONSTANT)); + assertFalse(isMember(CLASS)); + } + + @Test + void testIsMemberOnNull() { + assertFalse(isMember(null)); + } + + @Test + void testIsInitializer() { + assertTrue(isInitializer(STATIC_INIT)); + assertTrue(isInitializer(INSTANCE_INIT)); + assertFalse(isInitializer(METHOD)); + assertFalse(isInitializer(CONSTRUCTOR)); + assertFalse(isInitializer(CLASS)); + } + + @Test + void testIsInitializerOnNull() { + assertFalse(isInitializer(null)); + } + + @Test + void testIsVariable() { + assertTrue(isVariable(ENUM_CONSTANT)); + assertTrue(isVariable(FIELD)); + assertTrue(isVariable(PARAMETER)); + assertTrue(isVariable(LOCAL_VARIABLE)); + assertTrue(isVariable(EXCEPTION_PARAMETER)); + assertTrue(isVariable(RESOURCE_VARIABLE)); + assertFalse(isVariable(CLASS)); + } + + @Test + void testIsVariableOnNull() { + assertFalse(isVariable(null)); + } + + @Test + void testToElementKind() { + for (ElementType elementType : values()) { + assertElementKind(elementType); + } + } + + @Test + void testToElementKindOnNull() { + assertSame(OTHER, toElementKind(null)); + } + + @Test + void testMatchesElementType() { + for (ElementType elementType : values()) { + assertMatchesElementType(elementType); + } + } + + @Test + void testMatchesElementTypeOnNull() { + assertFalse(matchesElementType(null, (ElementType) null)); + assertFalse(matchesElementType(null, TYPE_USE)); + assertFalse(matchesElementType(toElementKind(TYPE_USE), (ElementType) null)); + } + + @Test + void testMatchesElementTypeWithArray() { + for (ElementType elementType : values()) { + assertTrue(matchesElementType(toElementKind(elementType), values())); + } + } + + @Test + void testMatchesElementTypeWithElement() { + matchesElementType(this.testTypeElement, TYPE); + } + + @Test + void testMatchesElementTypeWithArrayOnNull() { + assertFalse(matchesElementType(NULL_ELEMENT_KIND)); + assertFalse(matchesElementType(NULL_ELEMENT)); + + assertFalse(matchesElementType(NULL_ELEMENT_KIND, (ElementType[]) null)); + assertFalse(matchesElementType(NULL_ELEMENT, (ElementType[]) null)); + + assertFalse(matchesElementType(NULL_ELEMENT_KIND, TYPE_USE, PACKAGE)); + assertFalse(matchesElementType(NULL_ELEMENT, TYPE_USE, PACKAGE)); + + assertFalse(matchesElementType(toElementKind(TYPE_USE), (ElementType[]) null)); + } + + @Test + void testFilterElements() { + assertEmptyList(filterElements(ofList(testTypeElement), alwaysFalse())); + } + + @Test + void testFilterElementsOnNull() { + assertEmptyList(filterElements(NULL_LIST, alwaysTrue())); + List methods = ofList(echoMethod); + assertSame(emptyList(), filterElements(methods, NULL_PREDICATE_ARRAY)); + } + + @Test + void testFilterElementsOnEmpty() { + assertEmptyList(filterElements(emptyList(), alwaysTrue())); + List methods = ofList(echoMethod); + assertEquals(methods, filterElements(methods)); + } + + @Test + void testMatchParameterTypes() { + assertTrue(matchParameterTypes(echoMethod.getParameters(), String.class)); + assertFalse(matchParameterTypes(echoMethod.getParameters(), Object.class)); + } + + @Test + void testMatchParameterTypesOnNull() { + assertFalse(matchParameterTypes(NULL_LIST, String.class)); + assertFalse(matchParameterTypes(emptyList(), NULL_CLASS_ARRAY)); + } + + @Test + void testMatchParameterTypeNames() { + assertTrue(matchParameterTypeNames(echoMethod.getParameters(), "java.lang.String")); + assertFalse(matchParameterTypeNames(echoMethod.getParameters(), "java.lang.Object")); + } + + @Test + void testMatchParameterTypeNamesOnNull() { + assertFalse(matchParameterTypeNames(NULL_LIST, "java.lang.String")); + assertFalse(matchParameterTypeNames(emptyList(), NULL_STRING_ARRAY)); + } + + void assertElementKind(ElementType elementType) { + ElementKind elementKind = toElementKind(elementType); + assertNotNull(elementKind); + } + + void assertMatchesElementType(ElementType elementType) { + assertTrue(matchesElementType(toElementKind(elementType), elementType)); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ExecutableElementComparatorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ExecutableElementComparatorTest.java new file mode 100644 index 000000000..de416226f --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ExecutableElementComparatorTest.java @@ -0,0 +1,76 @@ +package io.microsphere.lang.model.util; + +import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; +import io.microsphere.test.service.TestService; +import org.junit.jupiter.api.Test; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import java.lang.reflect.Type; + +import static io.microsphere.lang.model.util.ExecutableElementComparator.INSTANCE; +import static io.microsphere.lang.model.util.MethodUtils.findMethod; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link ExecutableElementComparator} Test + * + * @author Mercy + * @see ExecutableElementComparator + * @since 1.0.0 + */ +class ExecutableElementComparatorTest extends UtilTest { + + private final ExecutableElementComparator comparator = INSTANCE; + + @Test + void testCompareOnSameMethods() { + // Object#toString() + String methodName = "toString"; + ExecutableElement method = getMethod(methodName); + assertEquals(0, comparator.compare(method, method)); + } + + @Test + void testCompareOnDifferentMethods() { + assertEquals("toString".compareTo("hashCode"), comparator.compare(getMethod("toString"), getMethod("hashCode"))); + } + + @Test + void testCompareOnOverloadMethodsWithSameParameterCount() { + // Integer#valueOf(int) | Integer#valueOf(String) + TypeElement typeElement = getTypeElement(Integer.class); + String methodName = "valueOf"; + assertEquals(int.class.getName().compareTo(String.class.getName()), comparator.compare(findMethod(typeElement, methodName, int.class), findMethod(typeElement, methodName, String.class))); + } + + @Test + void testCompareOnOverloadMethodsWithDifferentParameterCount() { + // StringBuilder#append(char[]) | StringBuilder#append(char[],int,int) + TypeElement typeElement = getTypeElement(StringBuilder.class); + String methodName = "append"; + assertEquals(-2, comparator.compare( + findMethod(typeElement, methodName, char[].class), + findMethod(typeElement, methodName, char[].class, int.class, int.class))); + } + + @Test + void testCompare() { + // AutoCloseable#close() + assertEquals(0, comparator.compare(getMethod("close"), + findMethod(getTypeElement(AutoCloseable.class), "close"))); + + // TestService#echo(String) + assertEquals(0, comparator.compare(getMethod("echo", String.class), + findMethod(getTypeElement(TestService.class), "echo", String.class))); + } + + @Override + public boolean equals(Object object) { + return super.equals(object); + } + + private ExecutableElement getMethod(String methodName, Type... parameterTypes) { + return findMethod(testTypeElement, methodName, parameterTypes); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/FieldUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/FieldUtilsTest.java new file mode 100644 index 000000000..afa535fb0 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/FieldUtilsTest.java @@ -0,0 +1,412 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.test.model.Color; +import io.microsphere.test.model.Model; +import org.junit.jupiter.api.Test; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import java.io.Serializable; +import java.lang.annotation.ElementType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static io.microsphere.lang.function.Predicates.alwaysFalse; +import static io.microsphere.lang.function.Predicates.alwaysTrue; +import static io.microsphere.lang.model.util.FieldUtils.equalsFieldName; +import static io.microsphere.lang.model.util.FieldUtils.filterDeclaredFields; +import static io.microsphere.lang.model.util.FieldUtils.findAllDeclaredFields; +import static io.microsphere.lang.model.util.FieldUtils.findDeclaredFields; +import static io.microsphere.lang.model.util.FieldUtils.findField; +import static io.microsphere.lang.model.util.FieldUtils.getAllDeclaredFields; +import static io.microsphere.lang.model.util.FieldUtils.getAllNonStaticFields; +import static io.microsphere.lang.model.util.FieldUtils.getDeclaredField; +import static io.microsphere.lang.model.util.FieldUtils.getDeclaredFields; +import static io.microsphere.lang.model.util.FieldUtils.getNonStaticFields; +import static io.microsphere.lang.model.util.FieldUtils.isEnumMemberField; +import static io.microsphere.lang.model.util.FieldUtils.isField; +import static io.microsphere.lang.model.util.FieldUtils.isNonStaticField; +import static io.microsphere.lang.model.util.MethodUtils.findMethod; +import static io.microsphere.util.StringUtils.EMPTY_STRING; +import static javax.lang.model.element.Modifier.FINAL; +import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.element.Modifier.PUBLIC; +import static javax.lang.model.element.Modifier.STATIC; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link FieldUtils} Test + * + * @author Mercy + * @since 1.0.0 + */ +class FieldUtilsTest extends UtilTest { + + @Test + void testGetDeclaredField() { + TypeElement type = getTypeElement(Model.class); + testGetDeclaredField(type, "f", float.class); + testGetDeclaredField(type, "d", double.class); + testGetDeclaredField(type, "tu", TimeUnit.class); + testGetDeclaredField(type, "str", String.class); + testGetDeclaredField(type, "bi", BigInteger.class); + testGetDeclaredField(type, "bd", BigDecimal.class); + } + + @Test + void testGetDeclaredFieldOnNotFound() { + TypeElement type = getTypeElement(Model.class); + assertNull(getDeclaredField(type, "b")); + assertNull(getDeclaredField(type, "s")); + assertNull(getDeclaredField(type, "i")); + assertNull(getDeclaredField(type, "l")); + assertNull(getDeclaredField(type, "z")); + } + + @Test + void testGetDeclaredFieldOnNull() { + assertNull(getDeclaredField(NULL_ELEMENT, "z")); + assertNull(getDeclaredField(NULL_TYPE_MIRROR, "z")); + } + + @Test + void testGetDeclaredFields() { + TypeElement type = getTypeElement(Model.class); + List fields = getDeclaredFields(type); + assertModelFields(fields); + + fields = getDeclaredFields(type.asType()); + assertModelFields(fields); + } + + @Test + void testGetDeclaredFieldsOnNull() { + assertTrue(getDeclaredFields(NULL_ELEMENT).isEmpty()); + assertTrue(getDeclaredFields(NULL_TYPE_MIRROR).isEmpty()); + } + + @Test + void testGetAllDeclaredFields() { + TypeElement type = getTypeElement(Model.class); + List fields = getAllDeclaredFields(type); + assertModelAllFields(fields); + } + + @Test + void testGetAllDeclaredFieldsOnNull() { + assertTrue(getAllDeclaredFields(NULL_ELEMENT).isEmpty()); + assertTrue(getAllDeclaredFields(NULL_TYPE_MIRROR).isEmpty()); + } + + @Test + void testFindField() { + TypeElement type = getTypeElement(Model.class); + testFindField(type, "f", float.class); + testFindField(type, "d", double.class); + testFindField(type, "tu", TimeUnit.class); + testFindField(type, "str", String.class); + testFindField(type, "bi", BigInteger.class); + testFindField(type, "bd", BigDecimal.class); + testFindField(type, "b", byte.class); + testFindField(type, "s", short.class); + testFindField(type, "i", int.class); + testFindField(type, "l", long.class); + testFindField(type, "z", boolean.class); + } + + @Test + void testFindFieldOnNull() { + TypeElement type = getTypeElement(Model.class); + assertNull(findField(NULL_ELEMENT, "f")); + assertNull(findField(NULL_ELEMENT, NULL_STRING)); + + assertNull(findField(NULL_TYPE_MIRROR, "f")); + assertNull(findField(NULL_TYPE_MIRROR, NULL_STRING)); + + assertNull(findField(type, NULL_STRING)); + assertNull(findField(type.asType(), NULL_STRING)); + } + + @Test + void testFindDeclaredFields() { + TypeElement type = getTypeElement(Model.class); + + List fields = findAllDeclaredFields(type, alwaysTrue()); + assertModelAllFields(fields); + + fields = findAllDeclaredFields(type, alwaysFalse()); + assertEmptyList(fields); + + fields = findDeclaredFields(type, f -> "f".equals(f.getSimpleName().toString())); + assertEquals(1, fields.size()); + assertEquals("f", fields.get(0).getSimpleName().toString()); + } + + @Test + void testFindDeclaredFieldsOnNull() { + assertEmptyList(findDeclaredFields(NULL_ELEMENT, alwaysTrue())); + assertEmptyList(findDeclaredFields(NULL_TYPE_MIRROR, alwaysTrue())); + } + + @Test + void testFindAllDeclaredFields() { + TypeElement type = getTypeElement(Model.class); + + List fields = findAllDeclaredFields(type, alwaysTrue()); + assertModelAllFields(fields); + + fields = findAllDeclaredFields(type, alwaysFalse()); + assertEmptyList(fields); + + fields = findAllDeclaredFields(type, f -> "f".equals(f.getSimpleName().toString())); + assertEquals(1, fields.size()); + assertEquals("f", fields.get(0).getSimpleName().toString()); + } + + @Test + void testFindAllDeclaredFieldsOnNull() { + assertEmptyList(findAllDeclaredFields(NULL_ELEMENT, alwaysTrue())); + assertEmptyList(findAllDeclaredFields(NULL_TYPE_MIRROR, alwaysTrue())); + } + + @Test + void testFilterDeclaredFieldsOnNull() { + assertFilterDeclaredFieldsReturningEmptyList(NULL_TYPE_MIRROR); + } + + @Test + void testFilterDeclaredFields() { + TypeMirror type = getTypeMirror(Model.class); + List fields = filterDeclaredFields(type, true, alwaysTrue()); + assertModelAllFields(fields); + + fields = filterDeclaredFields(type, true, alwaysFalse()); + assertEmptyList(fields); + + fields = filterDeclaredFields(type, false, alwaysTrue()); + assertModelFields(fields); + + fields = filterDeclaredFields(type, false, alwaysFalse()); + assertEmptyList(fields); + } + + @Test + void testFilterDeclaredFieldsOnNoDeclaredMembers() { + TypeMirror type = getTypeMirror(Serializable.class); + assertFilterDeclaredFieldsReturningEmptyList(type); + } + + @Test + void testFilterDeclaredFieldsOnNoDeclaredFields() { + TypeMirror type = getTypeMirror(Object.class); + assertFilterDeclaredFieldsReturningEmptyList(type); + } + + private void assertFilterDeclaredFieldsReturningEmptyList(TypeMirror type) { + assertEmptyList(filterDeclaredFields(type, true, alwaysTrue())); + assertEmptyList(filterDeclaredFields(type, false, alwaysTrue())); + assertEmptyList(filterDeclaredFields(type, true, alwaysFalse())); + assertEmptyList(filterDeclaredFields(type, false, alwaysFalse())); + assertEmptyList(filterDeclaredFields(type, true, NULL_PREDICATE_ARRAY)); + assertEmptyList(filterDeclaredFields(type, false, NULL_PREDICATE_ARRAY)); + assertEmptyList(filterDeclaredFields(type, true)); + assertEmptyList(filterDeclaredFields(type, false)); + } + + @Test + void testIsEnumField() { + TypeElement type = getTypeElement(Color.class); + + VariableElement field = findField(type, "RED"); + assertTrue(isEnumMemberField(field)); + + field = findField(type, "YELLOW"); + assertTrue(isEnumMemberField(field)); + + field = findField(type, "BLUE"); + assertTrue(isEnumMemberField(field)); + + type = getTypeElement(Model.class); + field = findField(type, "f"); + assertFalse(isEnumMemberField(field)); + + assertFalse(isEnumMemberField(NULL_FIELD)); + } + + @Test + void testIsNonStaticField() { + TypeElement type = getTypeElement(Model.class); + assertTrue(isNonStaticField(findField(type, "f"))); + } + + @Test + void testIsNonStaticFieldOnStaticField() { + TypeElement type = getTypeElement(Color.class); + for (Color color : Color.values()) { + assertFalse(isNonStaticField(findField(type, color.name()))); + } + } + + @Test + void testIsNonStaticFieldOnMethod() { + TypeElement type = getTypeElement(Model.class); + ExecutableElement method = findMethod(type, "setF", float.class); + for (VariableElement parameter : method.getParameters()) { + assertFalse(isNonStaticField(parameter)); + } + } + + @Test + void testIsField() { + TypeElement type = getTypeElement(Model.class); + assertTrue(isField(findField(type, "f"))); + assertTrue(isField(findField(type, "f"), PRIVATE)); + + type = getTypeElement(Color.class); + assertTrue(isField(findField(type, "BLUE"), PUBLIC, STATIC, FINAL)); + } + + @Test + void testIsFieldOnMethod() { + TypeElement type = getTypeElement(Model.class); + ExecutableElement method = findMethod(type, "getF"); + for (VariableElement parameter : method.getParameters()) { + assertFalse(isField(parameter)); + } + } + + @Test + void testIsFieldOnNull() { + assertFalse(isField(NULL_FIELD)); + assertFalse(isField(NULL_FIELD, PUBLIC, STATIC, FINAL)); + + TypeElement type = getTypeElement(Model.class); + assertFalse(isField(findField(type, "f"), NULL_MODIFIER_ARRAY)); + } + + @Test + void testGetNonStaticFields() { + TypeElement type = getTypeElement(Model.class); + + List fields = getNonStaticFields(type); + assertModelFields(fields); + + fields = getNonStaticFields(type.asType()); + assertModelFields(fields); + + assertTrue(getAllNonStaticFields(NULL_ELEMENT).isEmpty()); + assertTrue(getAllNonStaticFields(NULL_TYPE_MIRROR).isEmpty()); + } + + @Test + void testGetNonStaticFieldsOnNull() { + assertTrue(getNonStaticFields(NULL_TYPE_MIRROR).isEmpty()); + assertTrue(getNonStaticFields(NULL_ELEMENT).isEmpty()); + } + + @Test + void testGetNonStaticFieldsOnEnum() { + TypeElement type = getTypeElement(ElementType.class); + List fields = getNonStaticFields(type); + assertEmptyList(fields); + } + + @Test + void testGetAllNonStaticFields() { + TypeElement type = getTypeElement(Model.class); + + List fields = getAllNonStaticFields(type); + assertModelAllFields(fields); + + fields = getAllNonStaticFields(type.asType()); + assertModelAllFields(fields); + + assertTrue(getAllNonStaticFields(NULL_ELEMENT).isEmpty()); + assertTrue(getAllNonStaticFields(NULL_TYPE_MIRROR).isEmpty()); + } + + @Test + void testEqualsFieldName() { + TypeElement type = getTypeElement(Model.class); + String fieldName = "f"; + VariableElement field = findField(type, fieldName); + assertTrue(equalsFieldName(field, fieldName)); + assertFalse(equalsFieldName(field, "d")); + } + + @Test + void testEqualsFieldNameOnNull() { + TypeElement type = getTypeElement(Model.class); + String fieldName = "f"; + VariableElement field = findField(type, fieldName); + + assertFalse(equalsFieldName(NULL_FIELD, EMPTY_STRING)); + assertFalse(equalsFieldName(field, NULL_STRING)); + } + + private void assertModelFields(List fields) { + assertEquals(6, fields.size()); + assertEquals("d", fields.get(1).getSimpleName().toString()); + assertEquals("tu", fields.get(2).getSimpleName().toString()); + assertEquals("str", fields.get(3).getSimpleName().toString()); + assertEquals("bi", fields.get(4).getSimpleName().toString()); + assertEquals("bd", fields.get(5).getSimpleName().toString()); + } + + private void assertModelAllFields(List fields) { + assertEquals(11, fields.size()); + assertEquals("f", fields.get(0).getSimpleName().toString()); + assertEquals("d", fields.get(1).getSimpleName().toString()); + assertEquals("tu", fields.get(2).getSimpleName().toString()); + assertEquals("str", fields.get(3).getSimpleName().toString()); + assertEquals("bi", fields.get(4).getSimpleName().toString()); + assertEquals("bd", fields.get(5).getSimpleName().toString()); + assertEquals("b", fields.get(6).getSimpleName().toString()); + assertEquals("s", fields.get(7).getSimpleName().toString()); + assertEquals("i", fields.get(8).getSimpleName().toString()); + assertEquals("l", fields.get(9).getSimpleName().toString()); + assertEquals("z", fields.get(10).getSimpleName().toString()); + } + + private void testGetDeclaredField(TypeElement type, String fieldName, Type fieldType) { + VariableElement field = getDeclaredField(type, fieldName); + assertField(field, fieldName, fieldType); + + field = getDeclaredField(type.asType(), fieldName); + assertField(field, fieldName, fieldType); + } + + private void testFindField(TypeElement type, String fieldName, Type fieldType) { + VariableElement field = findField(type, fieldName); + assertField(field, fieldName, fieldType); + } + + private void assertField(VariableElement field, String fieldName, Type fieldType) { + assertEquals(fieldName, field.getSimpleName().toString()); + assertEquals(fieldType.getTypeName(), field.asType().toString()); + } +} diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java new file mode 100644 index 000000000..6e4246323 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java @@ -0,0 +1,135 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + + +import io.microsphere.test.annotation.TestAnnotation; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; + +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; +import java.lang.reflect.Method; +import java.util.Map; + +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValue; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValues; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link JSONAnnotationValueVisitor} Test + * + * @author Mercy + * @see JSONAnnotationValueVisitor + * @since 1.0.0 + */ +class JSONAnnotationValueVisitorTest extends UtilTest { + + private StringBuilder jsonBuilder; + + private JSONAnnotationValueVisitor visitor; + + private Map testAnnotationAttributes; + + @Override + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + this.jsonBuilder = new StringBuilder(); + this.visitor = new JSONAnnotationValueVisitor(jsonBuilder); + this.testAnnotationAttributes = getElementValues(testTypeElement, TestAnnotation.class); + } + + @Test + void testVisitBoolean() { + testVisit("z", "\"z\":true"); + } + + @Test + void testVisitByte() { + testVisit("b", "\"b\":1"); + } + + @Test + void testVisitChar() { + testVisit("c", "\"c\":\"b\""); + } + + @Test + void testVisitDouble() { + testVisit("d", "\"d\":1.0"); + } + + @Test + void testVisitFloat() { + testVisit("f", "\"f\":1.0"); + } + + @Test + void testVisitInt() { + testVisit("i", "\"i\":1"); + } + + @Test + void testVisitLong() { + testVisit("l", "\"l\":1"); + } + + @Test + void testVisitShort() { + testVisit("s", "\"s\":1"); + } + + @Test + void testVisitString() { + testVisit("string", "\"string\":\"testService\""); + } + + @Test + void testVisitType() { + testVisit("type", "\"type\":\"io.microsphere.annotation.processor.GenericTestService\""); + } + + @Test + void testVisitEnumConstant() { + testVisit("timeUnit", "\"timeUnit\":\"HOURS\""); + } + + @Test + void testVisitAnnotation() { + testVisit("since", "\"since\":{\"module\":\"\",\"value\":\"1.0.0\"}"); + } + + @Test + void testVisitArray() { + testVisit("properties", "\"properties\":[{\"name\":\"key\",\"type\":\"java.lang.String\",\"defaultValue\":\"default-value\",\"required\":true,\"description\":\"description\",\"source\":[]},{\"name\":\"key2\",\"type\":\"java.lang.Integer\",\"defaultValue\":\"default-value2\",\"required\":true,\"description\":\"description2\",\"source\":[]},{\"name\":\"key3\",\"type\":\"java.lang.Class\",\"defaultValue\":\"default-value3\",\"required\":true,\"description\":\"description3\",\"source\":[]}]"); + } + + @Test + void testVisitUnknown() { + assertSame(this.jsonBuilder, visitor.visitUnknown(null, null)); + } + + void testVisit(String attributeName, String expectedJson) { + Map.Entry elementValue = getElementValue(this.testAnnotationAttributes, attributeName); + ExecutableElement attributeMethod = elementValue.getKey(); + AnnotationValue annotationValue = elementValue.getValue(); + StringBuilder jsonBuilder = visitor.visit(annotationValue, attributeMethod); + assertEquals(expectedJson, jsonBuilder.toString()); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java new file mode 100644 index 000000000..4cc8234f9 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java @@ -0,0 +1,254 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + + +import io.microsphere.test.annotation.TestAnnotation; +import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; +import io.microsphere.test.model.Color; +import io.microsphere.test.model.StringArrayList; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import java.io.Serializable; +import java.util.List; + +import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; +import static java.lang.Boolean.TRUE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link JSONElementVisitor} Test + * + * @author Mercy + * @see JSONElementVisitor + * @since 1.0.0 + */ +class JSONElementVisitorTest extends UtilTest { + + private boolean supported; + + private StringBuilder jsonBuilder; + + private JSONElementVisitor visitor; + + + @BeforeEach + void setUp() { + this.supported = true; + this.jsonBuilder = new StringBuilder(); + this.visitor = new JSONElementVisitor() { + + @Override + protected boolean supports(Element e) { + super.supports(e); + return supported; + } + + @Override + protected boolean doVisitPackage(PackageElement e, StringBuilder jsonBuilder) { + super.doVisitPackage(e, jsonBuilder); + jsonBuilder.append("visitPackage"); + return TRUE; + } + + @Override + protected boolean doVisitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { + super.doVisitTypeParameter(e, jsonBuilder); + jsonBuilder.append("visitTypeParameter"); + return TRUE; + } + + @Override + public Boolean visitVariableAsEnumConstant(VariableElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitVariableAsEnumConstant"); + return TRUE; + } + + @Override + public Boolean visitVariableAsField(VariableElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitVariableAsField"); + return TRUE; + } + + @Override + public Boolean visitVariableAsParameter(VariableElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitVariableAsParameter"); + return TRUE; + } + + @Override + public Boolean visitExecutableAsConstructor(ExecutableElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitExecutableAsConstructor"); + return TRUE; + } + + @Override + public Boolean visitExecutableAsMethod(ExecutableElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitExecutableAsMethod"); + return TRUE; + } + + @Override + public Boolean visitTypeAsInterface(TypeElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitTypeAsInterface"); + return TRUE; + } + + @Override + public Boolean visitTypeAsEnum(TypeElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitTypeAsEnum"); + return TRUE; + } + + @Override + public Boolean visitTypeAsClass(TypeElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitTypeAsClass"); + return TRUE; + } + + @Override + public Boolean visitTypeAsAnnotationType(TypeElement e, StringBuilder stringBuilder) { + jsonBuilder.append("visitTypeAsAnnotationType"); + return TRUE; + } + }; + } + + @Test + void testVisitPackage() { + assertTrue(visitor.visitPackage(this.elements.getPackageElement("io.microsphere.annotation.processor.model.util"), jsonBuilder)); + assertJson("visitPackage"); + } + + @Test + void testVisitVariableOnUnsupported() { + supported = false; + assertFalse(visitor.visitVariable(null, jsonBuilder)); + } + + @Test + void testVisitVariableAsEnumConstant() { + VariableElement element = getField(Color.class, "RED"); + assertTrue(visitor.visitVariable(element, jsonBuilder)); + assertJson("visitVariableAsEnumConstant"); + + } + + @Test + void testVisitExecutableAsField() { + VariableElement element = getField(testClass, "context"); + assertTrue(visitor.visitVariable(element, jsonBuilder)); + assertJson("visitVariableAsField"); + } + + @Test + void testVisitVariableAsParameter() { + ExecutableElement method = getMethod(testClass, "echo", String.class); + for (VariableElement parameter : method.getParameters()) { + assertTrue(visitor.visitVariable(parameter, jsonBuilder)); + assertJson("visitVariableAsParameter"); + } + } + + @Test + void testVisitExecutableOnUnsupported() { + supported = false; + assertFalse(visitor.visitExecutable(null, jsonBuilder)); + } + + @Test + void testVisitExecutableAsConstructor() { + ExecutableElement constructor = getConstructor(testClass); + assertTrue(visitor.visitExecutable(constructor, jsonBuilder)); + assertJson("visitExecutableAsConstructor"); + } + + @Test + void testVisitExecutableAsMethod() { + ExecutableElement method = getMethod(testClass, "echo", String.class); + assertTrue(visitor.visitExecutable(method, jsonBuilder)); + assertJson("visitExecutableAsMethod"); + } + + @Test + void testVisitTypeOnUnsupported() { + supported = false; + TypeElement typeElement = getTypeElement(Serializable.class); + assertFalse(visitor.visitType(typeElement, jsonBuilder)); + } + + @Test + void testVisitTypeAsInterface() { + TypeElement typeElement = getTypeElement(Serializable.class); + assertTrue(visitor.visitType(typeElement, jsonBuilder)); + assertJson("visitTypeAsInterface"); + } + + @Test + void testVisitTypeAsEnum() { + TypeElement typeElement = getTypeElement(Color.class); + assertTrue(visitor.visitType(typeElement, jsonBuilder)); + assertTrue(jsonBuilder.toString().startsWith("visitTypeAsEnum")); + } + + @Test + void testVisitTypeAsClass() { + TypeElement typeElement = getTypeElement(testClass); + assertTrue(visitor.visitType(typeElement, jsonBuilder)); + assertTrue(jsonBuilder.toString().startsWith("visitTypeAsClass")); + } + + @Test + void testVisitTypeAsAnnotationType() { + TypeElement typeElement = getTypeElement(TestAnnotation.class); + assertTrue(visitor.visitType(typeElement, jsonBuilder)); + assertTrue(jsonBuilder.toString().startsWith("visitTypeAsAnnotationType")); + } + + @Test + void testVisitTypeParameterOnUnsupported() { + supported = false; + assertFalse(visitor.visitTypeParameter(null, jsonBuilder)); + } + + @Test + void testVisitTypeParameter() { + TypeElement typeElement = getTypeElement(StringArrayList.class); + TypeMirror superclass = typeElement.getSuperclass(); + TypeElement superTypeElement = ofTypeElement(superclass); + List typeParameters = superTypeElement.getTypeParameters(); + for (TypeParameterElement typeParameter : typeParameters) { + assertTrue(visitor.visitTypeParameter(typeParameter, jsonBuilder)); + assertJson("visitTypeParameter"); + } + } + + void assertJson(String expected) { + assertEquals(expected, jsonBuilder.toString()); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/LoggerUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/LoggerUtilsTest.java new file mode 100644 index 000000000..7f916931b --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/LoggerUtilsTest.java @@ -0,0 +1,76 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import org.junit.jupiter.api.Test; + +import static io.microsphere.lang.model.util.LoggerUtils.LOGGER; +import static io.microsphere.lang.model.util.LoggerUtils.debug; +import static io.microsphere.lang.model.util.LoggerUtils.error; +import static io.microsphere.lang.model.util.LoggerUtils.info; +import static io.microsphere.lang.model.util.LoggerUtils.trace; +import static io.microsphere.lang.model.util.LoggerUtils.warn; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * {@link LoggerUtils} Test + * + * @author Mercy + * @since 1.0.0 + */ +class LoggerUtilsTest { + + @Test + void testLogger() { + assertNotNull(LOGGER); + } + + @Test + void testTrace() { + trace("Hello,World"); + trace("Hello,{}", "World"); + trace("{},{}", "Hello", "World"); + } + + @Test + void testDebug() { + debug("Hello,World"); + debug("Hello,{}", "World"); + debug("{},{}", "Hello", "World"); + } + + @Test + void testInfo() { + info("Hello,World"); + info("Hello,{}", "World"); + info("{},{}", "Hello", "World"); + } + + @Test + void testWarn() { + warn("Hello,World"); + warn("Hello,{}", "World"); + warn("{},{}", "Hello", "World"); + } + + @Test + void testError() { + error("Hello,World"); + error("Hello,{}", "World"); + error("{},{}", "Hello", "World"); + } +} diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MemberUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MemberUtilsTest.java new file mode 100644 index 000000000..bae09a873 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MemberUtilsTest.java @@ -0,0 +1,173 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.test.model.Model; +import org.junit.jupiter.api.Test; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import java.util.List; + +import static io.microsphere.lang.function.Predicates.alwaysFalse; +import static io.microsphere.lang.function.Predicates.alwaysTrue; +import static io.microsphere.lang.model.util.MemberUtils.findAllDeclaredMembers; +import static io.microsphere.lang.model.util.MemberUtils.findDeclaredMembers; +import static io.microsphere.lang.model.util.MemberUtils.getAllDeclaredMembers; +import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; +import static javax.lang.model.util.ElementFilter.fieldsIn; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link MemberUtils} Test + * + * @author Mercy + * @since 1.0.0 + */ +class MemberUtilsTest extends UtilTest { + + @Test + void testGetDeclaredMembers() { + assertGetDeclaredMembersOfModel(); + } + + @Test + void testGetDeclaredMembersOnNull() { + assertEmptyList(getDeclaredMembers(NULL_TYPE_ELEMENT)); + assertEmptyList(getDeclaredMembers(NULL_TYPE_MIRROR)); + } + + @Test + void testGetAllDeclaredMembers() { + assertGetAllDeclaredMembersOfModel(); + } + + @Test + void testGetAllDeclaredMembersOnNull() { + assertEmptyList(getAllDeclaredMembers(NULL_TYPE_ELEMENT)); + assertEmptyList(getAllDeclaredMembers(NULL_TYPE_MIRROR)); + } + + @Test + void testGetDeclaredMembersOnAll() { + assertGetAllDeclaredMembersOfModel(getDeclaredMembers(getDeclaredType(Model.class), true)); + assertGetAllDeclaredMembersOfModel(getDeclaredMembers(getTypeElement(Model.class), true)); + } + + @Test + void testGetDeclaredMembersOnNotAll() { + assertGetDeclaredMembersOfModel(getDeclaredMembers(getDeclaredType(Model.class), false)); + assertGetDeclaredMembersOfModel(getDeclaredMembers(getTypeElement(Model.class), false)); + } + + @Test + void testFindDeclaredMembers() { + assertFindDeclaredMembersOfModel(); + } + + @Test + void testFindDeclaredMembersOnNull() { + assertEmptyList(findDeclaredMembers(NULL_TYPE_ELEMENT, alwaysTrue())); + assertEmptyList(findDeclaredMembers(NULL_TYPE_ELEMENT, alwaysFalse())); + assertEmptyList(findDeclaredMembers(NULL_TYPE_MIRROR, alwaysTrue())); + assertEmptyList(findDeclaredMembers(NULL_TYPE_MIRROR, alwaysFalse())); + } + + @Test + void testFindAllDeclaredMembers() { + assertFindAllDeclaredMembersOfModel(); + } + + @Test + void testFindAllDeclaredMembersOnNull() { + assertEmptyList(findAllDeclaredMembers(NULL_TYPE_ELEMENT, alwaysTrue())); + assertEmptyList(findAllDeclaredMembers(NULL_TYPE_ELEMENT, alwaysFalse())); + assertEmptyList(findAllDeclaredMembers(NULL_TYPE_MIRROR, alwaysTrue())); + assertEmptyList(findAllDeclaredMembers(NULL_TYPE_MIRROR, alwaysFalse())); + } + + @Test + void testFindDeclaredMembersOnAll() { + assertGetAllDeclaredMembersOfModel(findDeclaredMembers(getDeclaredType(Model.class), true, alwaysTrue())); + assertGetAllDeclaredMembersOfModel(findDeclaredMembers(getTypeElement(Model.class), true, alwaysTrue())); + } + + @Test + void testFindDeclaredMembersOnNotAll() { + assertGetDeclaredMembersOfModel(findDeclaredMembers(getDeclaredType(Model.class), false, alwaysTrue())); + assertGetDeclaredMembersOfModel(findDeclaredMembers(getTypeElement(Model.class), false, alwaysTrue())); + } + + private void assertFindDeclaredMembersOfModel() { + TypeElement type = getTypeElement(Model.class); + assertGetDeclaredMembersOfModel(findDeclaredMembers(type, alwaysTrue(), alwaysTrue())); + assertGetDeclaredMembersOfModel(findDeclaredMembers(type.asType(), alwaysTrue())); + + assertEmptyList(findDeclaredMembers(type, alwaysFalse())); + assertEmptyList(findDeclaredMembers(type.asType(), alwaysFalse())); + } + + private void assertFindAllDeclaredMembersOfModel() { + TypeElement type = getTypeElement(Model.class); + assertGetAllDeclaredMembersOfModel(findAllDeclaredMembers(type, alwaysTrue(), alwaysTrue())); + assertGetAllDeclaredMembersOfModel(findAllDeclaredMembers(type.asType(), alwaysTrue())); + + assertEmptyList(findAllDeclaredMembers(type, alwaysFalse())); + assertEmptyList(findAllDeclaredMembers(type.asType(), alwaysFalse())); + } + + private void assertGetDeclaredMembersOfModel() { + TypeElement type = getTypeElement(Model.class); + assertGetDeclaredMembersOfModel(getDeclaredMembers(type)); + assertGetDeclaredMembersOfModel(getDeclaredMembers(type.asType())); + } + + private void assertGetDeclaredMembersOfModel(List members) { + List fields = fieldsIn(members); + assertEquals(19, members.size()); + assertEquals(6, fields.size()); + assertEquals("f", fields.get(0).getSimpleName().toString()); + assertEquals("d", fields.get(1).getSimpleName().toString()); + assertEquals("tu", fields.get(2).getSimpleName().toString()); + assertEquals("str", fields.get(3).getSimpleName().toString()); + assertEquals("bi", fields.get(4).getSimpleName().toString()); + assertEquals("bd", fields.get(5).getSimpleName().toString()); + } + + private void assertGetAllDeclaredMembersOfModel() { + TypeElement type = getTypeElement(Model.class); + assertGetAllDeclaredMembersOfModel(getAllDeclaredMembers(type)); + assertGetAllDeclaredMembersOfModel(getAllDeclaredMembers(type.asType())); + } + + private void assertGetAllDeclaredMembersOfModel(List members) { + List fields = fieldsIn(members); + assertEquals(11, fields.size()); + assertEquals("f", fields.get(0).getSimpleName().toString()); + assertEquals("d", fields.get(1).getSimpleName().toString()); + assertEquals("tu", fields.get(2).getSimpleName().toString()); + assertEquals("str", fields.get(3).getSimpleName().toString()); + assertEquals("bi", fields.get(4).getSimpleName().toString()); + assertEquals("bd", fields.get(5).getSimpleName().toString()); + assertEquals("b", fields.get(6).getSimpleName().toString()); + assertEquals("s", fields.get(7).getSimpleName().toString()); + assertEquals("i", fields.get(8).getSimpleName().toString()); + assertEquals("l", fields.get(9).getSimpleName().toString()); + assertEquals("z", fields.get(10).getSimpleName().toString()); + } +} diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java new file mode 100644 index 000000000..f91edab56 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java @@ -0,0 +1,81 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; + +import javax.annotation.processing.Messager; +import java.lang.reflect.Method; + +import static io.microsphere.lang.model.util.MessagerUtils.printError; +import static io.microsphere.lang.model.util.MessagerUtils.printMandatoryWarning; +import static io.microsphere.lang.model.util.MessagerUtils.printMessage; +import static io.microsphere.lang.model.util.MessagerUtils.printNote; +import static io.microsphere.lang.model.util.MessagerUtils.printWarning; +import static javax.tools.Diagnostic.Kind.OTHER; + +/** + * {@link MessagerUtils} Test + * + * @author Mercy + * @see MessagerUtils + * @see Messager + * @since 1.0.0 + */ +class MessagerUtilsTest extends UtilTest { + + private Messager messager; + + @Override + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + this.messager = this.processingEnv.getMessager(); + } + + @Test + void testPrintNote() { + printNote(this.processingEnv, "Hello, {}!", "printNote"); + printNote(this.messager, "Hello, {}!", "printNote"); + } + + @Test + void testPrintWarning() { + printWarning(this.processingEnv, "Hello, {}!", "printWarning"); + printWarning(this.messager, "Hello, {}!", "printWarning"); + } + + @Test + void testPrintMandatoryWarning() { + printMandatoryWarning(this.processingEnv, "Hello, {}!", "printMandatoryWarning"); + printMandatoryWarning(this.messager, "Hello, {}!", "printMandatoryWarning"); + } + + @Test + void testPrintError() { + printError(this.processingEnv, "Hello, {}!", "printError"); + printError(this.messager, "Hello, {}!", "printError"); + } + + @Test + void testPrintMessage() { + printMessage(this.processingEnv, OTHER, "Hello, {}!", "printMessage"); + printMessage(this.messager, OTHER, "Hello, {}!", "printMessage"); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java new file mode 100644 index 000000000..931c71b18 --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java @@ -0,0 +1,507 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.constants.Constants; +import io.microsphere.constants.PropertyConstants; +import io.microsphere.test.model.Model; +import io.microsphere.test.service.TestService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Set; + +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.lang.function.Predicates.alwaysFalse; +import static io.microsphere.lang.function.Predicates.alwaysTrue; +import static io.microsphere.lang.model.util.ElementUtils.isPublicNonStatic; +import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; +import static io.microsphere.lang.model.util.MethodUtils.filterMethods; +import static io.microsphere.lang.model.util.MethodUtils.findAllDeclaredMethods; +import static io.microsphere.lang.model.util.MethodUtils.findDeclaredMethods; +import static io.microsphere.lang.model.util.MethodUtils.findMethod; +import static io.microsphere.lang.model.util.MethodUtils.findPublicNonStaticMethods; +import static io.microsphere.lang.model.util.MethodUtils.getAllDeclaredMethods; +import static io.microsphere.lang.model.util.MethodUtils.getDeclaredMethods; +import static io.microsphere.lang.model.util.MethodUtils.getEnclosingElement; +import static io.microsphere.lang.model.util.MethodUtils.getMethodName; +import static io.microsphere.lang.model.util.MethodUtils.getMethodParameterTypeMirrors; +import static io.microsphere.lang.model.util.MethodUtils.getMethodParameterTypeNames; +import static io.microsphere.lang.model.util.MethodUtils.getOverrideMethod; +import static io.microsphere.lang.model.util.MethodUtils.getReturnTypeName; +import static io.microsphere.lang.model.util.MethodUtils.isMethod; +import static io.microsphere.lang.model.util.MethodUtils.isPublicNonStaticMethod; +import static io.microsphere.lang.model.util.MethodUtils.matches; +import static io.microsphere.reflect.TypeUtils.getTypeNames; +import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; +import static io.microsphere.util.ArrayUtils.ofArray; +import static java.util.Collections.emptyList; +import static javax.lang.model.element.ElementKind.METHOD; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link MethodUtils} Test + * + * @author Mercy + * @since 1.0.0 + */ +class MethodUtilsTest extends UtilTest { + + private List objectMethods; + + private int objectMethodsSize; + + @Override + protected void addCompiledClasses(Set> compiledClasses) { + compiledClasses.add(PropertyConstants.class); + } + + @Override + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + TypeElement type = getTypeElement(Object.class); + List methods = getDeclaredMethods(type); + this.objectMethods = methods; + this.objectMethodsSize = methods.size(); + } + + @Test + void testDeclaredMethods() { + TypeElement type = getTypeElement(Model.class); + List methods = getDeclaredMethods(type); + assertEquals(12, methods.size()); + + methods = getDeclaredMethods(type.asType()); + assertEquals(12, methods.size()); + } + + @Test + void testDeclaredMethodsOnNull() { + assertTrue(getDeclaredMethods(NULL_TYPE_ELEMENT).isEmpty()); + assertTrue(getDeclaredMethods(NULL_TYPE_MIRROR).isEmpty()); + } + + @Test + void testGetAllDeclaredMethods() { + TypeElement type = getTypeElement(Model.class); + List methods = getAllDeclaredMethods(type); + assertEquals(objectMethodsSize + 22, methods.size()); + + methods = getAllDeclaredMethods(type.asType()); + assertEquals(objectMethodsSize + 22, methods.size()); + } + + @Test + void testGetAllDeclaredMethodsOnNull() { + assertTrue(getAllDeclaredMethods(NULL_TYPE_ELEMENT).isEmpty()); + assertTrue(getAllDeclaredMethods(NULL_TYPE_MIRROR).isEmpty()); + } + + @Test + void testFindDeclaredMethods() { + List methods = findDeclaredMethods(testTypeElement, alwaysTrue()); + assertEquals(2, methods.size()); + + methods = findDeclaredMethods(testTypeMirror, alwaysTrue()); + assertEquals(2, methods.size()); + + methods = findDeclaredMethods(testTypeElement, alwaysFalse()); + assertEmptyList(methods); + + methods = findDeclaredMethods(testTypeMirror, alwaysFalse()); + assertEmptyList(methods); + } + + @Test + void testFindDeclaredMethodsOnNoMemberType() { + TypeElement typeElement = getTypeElement(Serializable.class); + List methods = findDeclaredMethods(typeElement, alwaysTrue()); + assertEmptyList(methods); + } + + @Test + void testFindDeclaredMethodsOnNoMethodType() { + TypeElement typeElement = getTypeElement(PropertyConstants.class); + List methods = findDeclaredMethods(typeElement, alwaysTrue()); + assertEmptyList(methods); + } + + @Test + void testFindAllDeclaredMethods() { + List methods = findAllDeclaredMethods(testTypeElement, alwaysTrue()); + assertEquals(objectMethodsSize + 14, methods.size()); + + methods = findAllDeclaredMethods(testTypeMirror, alwaysTrue()); + assertEquals(objectMethodsSize + 14, methods.size()); + + methods = findAllDeclaredMethods(testTypeElement, alwaysFalse()); + assertEmptyList(methods); + + methods = findAllDeclaredMethods(testTypeMirror, alwaysFalse()); + assertEmptyList(methods); + } + + @Test + void testFindAllDeclaredMethodsOnNoMemberType() { + TypeElement typeElement = getTypeElement(Serializable.class); + List methods = findAllDeclaredMethods(typeElement, alwaysTrue()); + assertEmptyList(methods); + } + + @Test + void testFindAllDeclaredMethodsOnNoMethodType() { + TypeElement typeElement = getTypeElement(Constants.class); + List methods = findAllDeclaredMethods(typeElement, alwaysTrue()); + assertEmptyList(methods); + } + + @Test + void testFindAllDeclaredMethodsOnNull() { + assertEmptyList(findAllDeclaredMethods(NULL_TYPE_ELEMENT, alwaysTrue())); + assertEmptyList(findAllDeclaredMethods(NULL_TYPE_ELEMENT, alwaysFalse())); + assertEmptyList(findAllDeclaredMethods(NULL_TYPE_MIRROR, alwaysTrue())); + assertEmptyList(findAllDeclaredMethods(NULL_TYPE_MIRROR, alwaysFalse())); + } + + @Test + void testFindAllDeclaredMethodsWithExcludedTypes() { + List methods = findAllDeclaredMethodsWithoutObjectType(); + assertEquals(14, methods.size()); + } + + @Test + void testFindAllDeclaredMethodsWithExcludedTypesOnNull() { + assertEmptyList(findAllDeclaredMethods(NULL_TYPE_ELEMENT, Object.class)); + assertEmptyList(findAllDeclaredMethods(NULL_TYPE_MIRROR, Object.class)); + } + + @Test + void testFindPublicNonStaticMethods() { + List methods = findPublicNonStaticMethods(testTypeElement, Object.class); + assertEquals(14, methods.size()); + + methods = findPublicNonStaticMethods(testTypeElement.asType(), Object.class); + assertEquals(14, methods.size()); + } + + @Test + void testFindPublicNonStaticMethodsOnNull() { + assertEmptyList(findPublicNonStaticMethods(NULL_TYPE_ELEMENT, Object.class)); + assertEmptyList(findPublicNonStaticMethods(NULL_TYPE_MIRROR, Object.class)); + } + + @Test + void testIsMethod() { + List members = getDeclaredMembers(testTypeElement); + for (Element member : members) { + if (member instanceof ExecutableElement) { + ExecutableElement element = (ExecutableElement) member; + assertEquals(METHOD == member.getKind(), isMethod(element)); + } + } + } + + @Test + void testIsMethodOnNull() { + assertFalse(isMethod(NULL_METHOD)); + } + + @Test + void testIsPublicNonStaticMethod() { + List members = getDeclaredMembers(testTypeElement); + for (Element member : members) { + if (member instanceof ExecutableElement) { + ExecutableElement element = (ExecutableElement) member; + switch (member.getKind()) { + case METHOD: + assertEquals(isPublicNonStaticMethod(element), isPublicNonStatic(element)); + break; + case CONSTRUCTOR: + assertFalse(isPublicNonStaticMethod(element)); + break; + } + } + } + + // Integer#valueOf(String) is a public static method + assertFalse(isPublicNonStaticMethod(findMethod(getTypeElement(Integer.class), "valueOf", String.class))); + + } + + @Test + void testIsPublicNonStaticMethodOnNull() { + assertFalse(isPublicNonStaticMethod(NULL_METHOD)); + } + + @Test + void testFindMethod() { + // Test methods from java.lang.Object + // Object#toString() + Type type = Model.class; + assertFindMethod(type, "toString"); + + // Object#hashCode() + assertFindMethod(type, "hashCode"); + + // Object#getClass() + assertFindMethod(type, "getClass"); + + // Object#finalize() + assertFindMethod(type, "finalize"); + + // Object#clone() + assertFindMethod(type, "clone"); + + // Object#notify() + assertFindMethod(type, "notify"); + + // Object#notifyAll() + assertFindMethod(type, "notifyAll"); + + // Object#wait(long) + assertFindMethod(type, "wait", long.class); + + // Object#wait(long,int) + assertFindMethod(type, "wait", long.class, int.class); + + // Object#equals(Object) + assertFindMethod(type, "equals", Object.class); + } + + @Test + void testFindMethodOnNotFound() { + assertNull(findMethod(testTypeElement, "notFound")); + assertNull(findMethod(testTypeElement, "notFound", String.class)); + assertNull(findMethod(testTypeElement, "notFound", "java.lang.String")); + + assertNull(findMethod(testTypeMirror, "notFound")); + assertNull(findMethod(testTypeMirror, "notFound", String.class)); + assertNull(findMethod(testTypeMirror, "notFound", "java.lang.String")); + } + + @Test + void testFindMethodOnNull() { + assertNull(findMethod(NULL_TYPE_ELEMENT, "toString")); + assertNull(findMethod(NULL_TYPE_ELEMENT, "toString", String.class)); + assertNull(findMethod(NULL_TYPE_ELEMENT, "toString", "java.lang.String")); + assertNull(findMethod(NULL_TYPE_ELEMENT, "toString", NULL_TYPE_ARRAY)); + assertNull(findMethod(NULL_TYPE_ELEMENT, "toString", NULL_STRING_ARRAY)); + + assertNull(findMethod(NULL_TYPE_MIRROR, "toString")); + assertNull(findMethod(NULL_TYPE_MIRROR, "toString", String.class)); + assertNull(findMethod(NULL_TYPE_MIRROR, "toString", "java.lang.String")); + assertNull(findMethod(NULL_TYPE_MIRROR, "toString", NULL_TYPE_ARRAY)); + assertNull(findMethod(NULL_TYPE_MIRROR, "toString", NULL_STRING_ARRAY)); + + assertNull(findMethod(testTypeElement, NULL_STRING)); + assertNull(findMethod(testTypeElement, NULL_STRING, String.class)); + assertNull(findMethod(testTypeElement, NULL_STRING, "java.lang.String")); + assertNull(findMethod(testTypeElement, NULL_STRING, NULL_TYPE_ARRAY)); + assertNull(findMethod(testTypeElement, NULL_STRING, NULL_STRING_ARRAY)); + + assertNull(findMethod(testTypeMirror, NULL_STRING)); + assertNull(findMethod(testTypeMirror, NULL_STRING, String.class)); + assertNull(findMethod(testTypeMirror, NULL_STRING, "java.lang.String")); + assertNull(findMethod(testTypeMirror, NULL_STRING, NULL_TYPE_ARRAY)); + assertNull(findMethod(testTypeMirror, NULL_STRING, NULL_STRING_ARRAY)); + + + assertNull(findMethod(testTypeElement, "toString", NULL_TYPE_ARRAY)); + assertNull(findMethod(testTypeElement, "toString", NULL_STRING_ARRAY)); + + assertNull(findMethod(testTypeMirror, "toString", NULL_TYPE_ARRAY)); + assertNull(findMethod(testTypeMirror, "toString", NULL_STRING_ARRAY)); + } + + @Test + void testGetOverrideMethod() { + List methods = findAllDeclaredMethodsWithoutObjectType(); + + ExecutableElement overrideMethod = getOverrideMethod(processingEnv, testTypeElement, methods.get(0)); + assertNull(overrideMethod); + + ExecutableElement declaringMethod = findMethod(getTypeElement(TestService.class), "echo", "java.lang.String"); + + overrideMethod = getOverrideMethod(processingEnv, testTypeElement, declaringMethod); + assertEquals(methods.get(0), overrideMethod); + } + + @Test + void testFilterMethods() { + + } + + @Test + void testFilterMethodsOnNull() { + assertEmptyList(filterMethods(NULL_LIST, alwaysTrue())); + assertEmptyList(filterMethods(NULL_LIST, NULL_PREDICATE_ARRAY)); + } + + @Test + void testFilterMethodsOnEmpty() { + assertEmptyList(filterMethods(emptyList(), alwaysTrue())); + assertEmptyList(filterMethods(emptyList(), NULL_PREDICATE_ARRAY)); + } + + @Test + void testFilterMethodsOnReturningEmptyList() { + List methods = getDeclaredMethods(testTypeElement); + assertEmptyList(filterMethods(methods, alwaysFalse())); + assertEquals(methods, filterMethods(methods)); + } + + @Test + void testGetMethodName() { + ExecutableElement method = findMethod(testTypeElement, "echo", "java.lang.String"); + assertEquals("echo", getMethodName(method)); + assertNull(getMethodName(NULL_METHOD)); + } + + @Test + void testGetMethodNameOnNull() { + assertNull(getMethodName(NULL_METHOD)); + } + + @Test + void testReturnTypeName() { + ExecutableElement method = findMethod(testTypeElement, "echo", "java.lang.String"); + assertEquals("java.lang.String", getReturnTypeName(method)); + } + + @Test + void testReturnTypeNameOnNull() { + assertNull(getReturnTypeName(NULL_METHOD)); + } + + @Test + void testMatchParameterTypeNames() { + String[] parameterTypeNames = ofArray("java.lang.String"); + ExecutableElement method = findMethod(testTypeElement, "echo", parameterTypeNames); + assertArrayEquals(parameterTypeNames, getMethodParameterTypeNames(method)); + } + + @Test + void testMatchParameterTypeNamesOnNull() { + assertSame(EMPTY_STRING_ARRAY, getMethodParameterTypeNames(NULL_METHOD)); + } + + @Test + void testMatchParameterTypes() { + ExecutableElement method = findMethod(testTypeElement, "toString"); + assertEmptyList(getMethodParameterTypeMirrors(method)); + + method = findMethod(testTypeElement, "equals", Object.class); + List parameterTypes = getMethodParameterTypeMirrors(method); + assertEquals(ofList(parameterTypes.toArray(EMPTY_TYPE_MIRROR_ARRAY)), parameterTypes); + } + + @Test + void testMatchParameterTypesOnNull() { + assertEmptyList(getMethodParameterTypeMirrors(NULL_METHOD)); + } + + @Test + void testMatches() { + assertFindMethod(testClass, "echo", String.class); + } + + @Test + void tstMatchesOnFalse() { + ExecutableElement method = findMethod(testTypeElement, "echo", String.class); + + Type[] parameterTypes = ofArray(String.class, String.class); + String[] parameterTypeNames = getTypeNames(parameterTypes); + assertFalse(matches(method, "echo", parameterTypes)); + assertFalse(matches(method, "echo", parameterTypeNames)); + + parameterTypes = ofArray(Object.class); + parameterTypeNames = getTypeNames(parameterTypes); + assertFalse(matches(method, "echo", parameterTypes)); + assertFalse(matches(method, "echo", parameterTypeNames)); + } + + @Test + void testMatchesOnNull() { + String methodName = "echo"; + Type[] parameterTypes = ofArray(String.class); + String[] parameterTypeNames = getTypeNames(parameterTypes); + ExecutableElement method = findMethod(testTypeElement, methodName, parameterTypes); + + assertFalse(matches(NULL_METHOD, NULL_STRING, parameterTypes)); + assertFalse(matches(method, NULL_STRING, parameterTypes)); + assertFalse(matches(method, methodName, NULL_TYPE_ARRAY)); + + assertFalse(matches(NULL_METHOD, NULL_STRING, parameterTypeNames)); + assertFalse(matches(method, NULL_STRING, parameterTypeNames)); + assertFalse(matches(method, methodName, NULL_STRING_ARRAY)); + } + + @Test + void testGetEnclosingElement() { + String methodName = "echo"; + Type[] parameterTypes = ofArray(String.class); + ExecutableElement method = findMethod(testTypeElement, methodName, parameterTypes); + assertSame(testTypeElement, getEnclosingElement(method)); + } + + @Test + void testGetEnclosingElementOnNull() { + assertNull(getEnclosingElement(NULL_METHOD)); + } + + private void assertFindMethod(Type type, String methodName, Type... parameterTypes) { + TypeElement typeElement = getTypeElement(type); + String[] parameterTypeNames = getTypeNames(parameterTypes); + + ExecutableElement method = findMethod(typeElement, methodName, parameterTypes); + assertMatchesMethod(method, methodName, parameterTypes); + + method = findMethod(typeElement, methodName, parameterTypeNames); + assertMatchesMethod(method, methodName, parameterTypes); + + method = findMethod(typeElement.asType(), methodName, parameterTypes); + assertMatchesMethod(method, methodName, parameterTypes); + + method = findMethod(typeElement.asType(), methodName, parameterTypeNames); + assertMatchesMethod(method, methodName, parameterTypeNames); + } + + private void assertMatchesMethod(ExecutableElement method, String methodName, Type... parameterTypes) { + assertTrue(matches(method, methodName, parameterTypes)); + } + + private void assertMatchesMethod(ExecutableElement method, String methodName, String... parameterTypeNames) { + assertTrue(matches(method, methodName, parameterTypeNames)); + } + + private List findAllDeclaredMethodsWithoutObjectType() { + return findAllDeclaredMethods(testTypeElement, Object.class); + } +} diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java new file mode 100644 index 000000000..a177dad1f --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java @@ -0,0 +1,216 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import io.microsphere.test.annotation.TestAnnotation; +import io.microsphere.test.service.GenericTestService; +import io.microsphere.test.service.TestService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; + +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.AnnotationValueVisitor; +import javax.lang.model.element.ExecutableElement; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValue; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValues; +import static java.util.Objects.deepEquals; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.stream.Stream.of; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link ResolvableAnnotationValueVisitor} Test + * + * @author Mercy + * @see ResolvableAnnotationValueVisitor + * @since 1.0.0 + */ +class ResolvableAnnotationValueVisitorTest extends UtilTest { + + private ResolvableAnnotationValueVisitor visitor; + + private ResolvableAnnotationValueVisitor visitor1; + + private ResolvableAnnotationValueVisitor visitor2; + + static final boolean BOOLEAN_VALUE = true; + + static final byte BYTE_VALUE = 1; + + static final char CHAR_VALUE = 'b'; + + static final double DOUBLE_VALUE = 1.0D; + + static final float FLOAT_VALUE = 1.0F; + + static final int INT_VALUE = 1; + + static final long LONG_VALUE = 1L; + + static final short SHORT_VALUE = 1; + + static final String STRING_VALUE = "testService"; + + static final Class TYPE_VALUE = GenericTestService.class; + + static final Class[] TYPES_VALUE = {TestService.class, AutoCloseable.class, Serializable.class}; + + private Map testAnnotationAttributes; + + @Override + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + this.visitor = new ResolvableAnnotationValueVisitor(); + this.visitor1 = new ResolvableAnnotationValueVisitor(true); + this.visitor2 = new ResolvableAnnotationValueVisitor(true, true); + this.testAnnotationAttributes = getElementValues(testTypeElement, TestAnnotation.class); + } + + @Test + void testVisitBoolean() { + assertEquals(BOOLEAN_VALUE, visitor.visitBoolean(BOOLEAN_VALUE, null)); + assertVisit(this.visitor, "z", BOOLEAN_VALUE); + assertVisit(this.visitor1, "z", BOOLEAN_VALUE); + assertVisit(this.visitor2, "z", BOOLEAN_VALUE); + } + + @Test + void testVisitByte() { + assertEquals(BYTE_VALUE, visitor.visitByte(BYTE_VALUE, null)); + assertVisit(this.visitor, "b", BYTE_VALUE); + assertVisit(this.visitor1, "b", BYTE_VALUE); + assertVisit(this.visitor2, "b", BYTE_VALUE); + } + + @Test + void testVisitChar() { + assertEquals(CHAR_VALUE, visitor.visitChar(CHAR_VALUE, null)); + assertVisit(this.visitor, "c", CHAR_VALUE); + assertVisit(this.visitor1, "c", CHAR_VALUE); + assertVisit(this.visitor2, "c", CHAR_VALUE); + } + + @Test + void testVisitDouble() { + assertEquals(DOUBLE_VALUE, visitor.visitDouble(DOUBLE_VALUE, null)); + assertVisit(this.visitor, "d", DOUBLE_VALUE); + assertVisit(this.visitor1, "d", DOUBLE_VALUE); + assertVisit(this.visitor2, "d", DOUBLE_VALUE); + } + + @Test + void testVisitFloat() { + assertEquals(FLOAT_VALUE, visitor.visitFloat(FLOAT_VALUE, null)); + assertVisit(this.visitor, "f", FLOAT_VALUE); + assertVisit(this.visitor1, "f", FLOAT_VALUE); + assertVisit(this.visitor2, "f", FLOAT_VALUE); + } + + @Test + void testVisitInt() { + assertEquals(INT_VALUE, visitor.visitInt(INT_VALUE, null)); + assertVisit(this.visitor, "i", INT_VALUE); + assertVisit(this.visitor1, "i", INT_VALUE); + assertVisit(this.visitor2, "i", INT_VALUE); + } + + @Test + void testVisitLong() { + assertEquals(LONG_VALUE, visitor.visitLong(LONG_VALUE, null)); + assertVisit(this.visitor, "l", LONG_VALUE); + assertVisit(this.visitor1, "l", LONG_VALUE); + assertVisit(this.visitor2, "l", LONG_VALUE); + } + + @Test + void testVisitShort() { + assertEquals(SHORT_VALUE, visitor.visitShort(SHORT_VALUE, null)); + assertVisit(this.visitor, "s", SHORT_VALUE); + assertVisit(this.visitor1, "s", SHORT_VALUE); + assertVisit(this.visitor2, "s", SHORT_VALUE); + } + + @Test + void testVisitString() { + assertEquals(STRING_VALUE, visitor.visitString(STRING_VALUE, null)); + assertVisit(this.visitor, "string", STRING_VALUE); + assertVisit(this.visitor1, "string", STRING_VALUE); + assertVisit(this.visitor1, "string", STRING_VALUE); + } + + @Test + void testVisitType() { + assertEquals(TYPE_VALUE, visitor.visitType(getTypeMirror(TYPE_VALUE), null)); + assertVisit(this.visitor, "type", TYPE_VALUE); + assertVisit(this.visitor1, "type", TYPE_VALUE.getName()); + assertVisit(this.visitor2, "type", TYPE_VALUE.getName()); + } + + @Test + void testVisitEnumConstant() { + assertVisit(this.visitor, "timeUnit", HOURS); + assertVisit(this.visitor1, "timeUnit", HOURS); + assertVisit(this.visitor2, "timeUnit", HOURS); + } + + @Test + void testVisitAnnotation() { + String attributeName = "since"; + + Map attributesMap = new LinkedHashMap<>(); + attributesMap.put("module", ""); + attributesMap.put("value", "1.0.0"); + + TestAnnotation testAnnotation = testClass.getAnnotation(TestAnnotation.class); + + assertVisit(this.visitor, attributeName, testAnnotation.since()); + assertVisit(this.visitor1, attributeName, testAnnotation.since()); + assertVisit(this.visitor2, attributeName, attributesMap); + } + + @Test + void testVisitArray() { + assertVisit(this.visitor, "types", TYPES_VALUE); + assertVisit(this.visitor1, "types", of(TYPES_VALUE).map(Class::getName).toArray(String[]::new)); + assertVisit(this.visitor2, "types", of(TYPES_VALUE).map(Class::getName).toArray(String[]::new)); + } + + @Test + void testVisitUnknown() { + for (Entry elementValue : this.testAnnotationAttributes.entrySet()) { + ExecutableElement attributeMethod = elementValue.getKey(); + AnnotationValue annotationValue = elementValue.getValue(); + assertSame(annotationValue, visitor.visitUnknown(annotationValue, attributeMethod)); + } + } + + void assertVisit(AnnotationValueVisitor visitor, String attributeName, Object expectedValue) { + Entry elementValue = getElementValue(this.testAnnotationAttributes, attributeName); + ExecutableElement attributeMethod = elementValue.getKey(); + AnnotationValue annotationValue = elementValue.getValue(); + assertTrue(deepEquals(expectedValue, annotationValue.accept(visitor, attributeMethod))); + } +} diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/TypeUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/TypeUtilsTest.java new file mode 100644 index 000000000..9e73f8b5c --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/TypeUtilsTest.java @@ -0,0 +1,1876 @@ +/* + * 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. + */ +package io.microsphere.lang.model.util; + +import io.microsphere.test.model.ArrayTypeModel; +import io.microsphere.test.model.CollectionTypeModel; +import io.microsphere.test.model.Color; +import io.microsphere.test.model.MapTypeModel; +import io.microsphere.test.model.Model; +import io.microsphere.test.model.PrimitiveTypeModel; +import io.microsphere.test.service.DefaultTestService; +import io.microsphere.test.service.GenericTestService; +import io.microsphere.test.service.TestService; +import io.microsphere.test.service.TestServiceImpl; +import org.junit.jupiter.api.Test; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import java.io.Serializable; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.EventListener; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; + +import static io.microsphere.collection.ListUtils.ofList; +import static io.microsphere.lang.function.Predicates.alwaysFalse; +import static io.microsphere.lang.function.Predicates.alwaysTrue; +import static io.microsphere.lang.model.util.FieldUtils.findField; +import static io.microsphere.lang.model.util.FieldUtils.getDeclaredFields; +import static io.microsphere.lang.model.util.MethodUtils.findMethod; +import static io.microsphere.lang.model.util.TypeUtils.findAllDeclaredTypes; +import static io.microsphere.lang.model.util.TypeUtils.findAllDeclaredTypesOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.findAllDeclaredTypesOfSuperTypes; +import static io.microsphere.lang.model.util.TypeUtils.findAllDeclaredTypesOfSuperclasses; +import static io.microsphere.lang.model.util.TypeUtils.findAllTypeElementsOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.findAllTypeElementsOfSuperclasses; +import static io.microsphere.lang.model.util.TypeUtils.findAllTypeMirrorsOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.findDeclaredTypes; +import static io.microsphere.lang.model.util.TypeUtils.findDeclaredTypesOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.findInterfaceTypeMirror; +import static io.microsphere.lang.model.util.TypeUtils.findTypeElements; +import static io.microsphere.lang.model.util.TypeUtils.findTypeElementsOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.findTypeMirrorsOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.getAllDeclaredTypes; +import static io.microsphere.lang.model.util.TypeUtils.getAllDeclaredTypesOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.getAllDeclaredTypesOfSuperTypes; +import static io.microsphere.lang.model.util.TypeUtils.getAllDeclaredTypesOfSuperclasses; +import static io.microsphere.lang.model.util.TypeUtils.getAllTypeElements; +import static io.microsphere.lang.model.util.TypeUtils.getAllTypeElementsOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.getAllTypeElementsOfSuperTypes; +import static io.microsphere.lang.model.util.TypeUtils.getAllTypeElementsOfSuperclasses; +import static io.microsphere.lang.model.util.TypeUtils.getAllTypeMirrorsOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.getDeclaredTypeOfSuperclass; +import static io.microsphere.lang.model.util.TypeUtils.getDeclaredTypes; +import static io.microsphere.lang.model.util.TypeUtils.getDeclaredTypesOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.getTypeElementOfSuperclass; +import static io.microsphere.lang.model.util.TypeUtils.getTypeElementsOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.getTypeMirrorsOfInterfaces; +import static io.microsphere.lang.model.util.TypeUtils.isAnnotationType; +import static io.microsphere.lang.model.util.TypeUtils.isArrayType; +import static io.microsphere.lang.model.util.TypeUtils.isClassType; +import static io.microsphere.lang.model.util.TypeUtils.isDeclaredType; +import static io.microsphere.lang.model.util.TypeUtils.isEnumType; +import static io.microsphere.lang.model.util.TypeUtils.isInterfaceType; +import static io.microsphere.lang.model.util.TypeUtils.isPrimitiveType; +import static io.microsphere.lang.model.util.TypeUtils.isSameType; +import static io.microsphere.lang.model.util.TypeUtils.isSimpleType; +import static io.microsphere.lang.model.util.TypeUtils.isTypeElement; +import static io.microsphere.lang.model.util.TypeUtils.ofDeclaredType; +import static io.microsphere.lang.model.util.TypeUtils.ofDeclaredTypes; +import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; +import static io.microsphere.lang.model.util.TypeUtils.ofTypeElements; +import static io.microsphere.lang.model.util.TypeUtils.ofTypeMirrors; +import static io.microsphere.lang.model.util.TypeUtils.typeElementFinder; +import static io.microsphere.reflect.TypeUtils.getTypeNames; +import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; +import static io.microsphere.util.ArrayUtils.combine; +import static io.microsphere.util.ArrayUtils.length; +import static io.microsphere.util.ArrayUtils.ofArray; +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The {@link TypeUtils} Test + * + * @author Mercy + * @since 1.0.0 + */ +class TypeUtilsTest extends UtilTest { + + /** + * self type + */ + private static final Class SELF_TYPE = TestServiceImpl.class; + + /** + * super class + */ + private static final Class SUPER_CLASS = GenericTestService.class; + + /** + * all types + */ + private static final Type[] ALL_TYPES = ofArray(SELF_TYPE, SUPER_CLASS, DefaultTestService.class, Object.class, TestService.class, EventListener.class, AutoCloseable.class, Serializable.class); + + /** + * all super types + */ + private static final Type[] ALL_SUPER_TYPES = ofArray(SUPER_CLASS, DefaultTestService.class, Object.class, TestService.class, EventListener.class, AutoCloseable.class, Serializable.class); + + /** + * all super classes + */ + private static final Type[] ALL_SUPER_CLASSES = ofArray(SUPER_CLASS, DefaultTestService.class, Object.class); + + /** + * all super interfaces + */ + private static final Type[] ALL_SUPER_INTERFACES = ofArray(TestService.class, EventListener.class, AutoCloseable.class, Serializable.class); + + /** + * super interfaces + */ + private static final Type[] SUPER_INTERFACES = ofArray(TestService.class, AutoCloseable.class, Serializable.class); + + /** + * super class + super interfaces + */ + private static final Type[] SUPER_TYPES = combine(SUPER_CLASS, SUPER_INTERFACES); + + /** + * self type + all super types = all types + */ + private static final Type[] SELF_TYPE_PLUS_ALL_SUPER_TYPES = combine(SELF_TYPE, ALL_SUPER_TYPES); + + /** + * self type + all super classes + */ + private static final Type[] SELF_TYPE_PLUS_ALL_SUPER_CLASSES = combine(SELF_TYPE, ALL_SUPER_CLASSES); + + /** + * self type + all super interfaces + */ + private static final Type[] SELF_TYPE_PLUS_ALL_SUPER_INTERFACES = combine(SELF_TYPE, ALL_SUPER_INTERFACES); + + /** + * self type + super class + */ + private static final Type[] SELF_TYPE_PLUS_SUPER_CLASS = ofArray(SELF_TYPE, SUPER_CLASS); + + /** + * self type + super interfaces + */ + private static final Type[] SELF_TYPE_PLUS_SUPER_INTERFACES = combine(SELF_TYPE, SUPER_INTERFACES); + + /** + * self type + super class + super interfaces + */ + private static final Type[] SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES = combine(SELF_TYPE, SUPER_TYPES); + + @Override + protected void addCompiledClasses(Set> compiledClasses) { + compiledClasses.add(ArrayTypeModel.class); + compiledClasses.add(CollectionTypeModel.class); + compiledClasses.add(Color.class); + compiledClasses.add(MapTypeModel.class); + } + + @Test + void testIsSimpleType() { + assertTrue(isSimpleType(getTypeElement(Void.class))); + assertTrue(isSimpleType(getTypeElement(Boolean.class))); + assertTrue(isSimpleType(getTypeElement(Character.class))); + assertTrue(isSimpleType(getTypeElement(Byte.class))); + assertTrue(isSimpleType(getTypeElement(Short.class))); + assertTrue(isSimpleType(getTypeElement(Integer.class))); + assertTrue(isSimpleType(getTypeElement(Long.class))); + assertTrue(isSimpleType(getTypeElement(Float.class))); + assertTrue(isSimpleType(getTypeElement(Double.class))); + assertTrue(isSimpleType(getTypeElement(String.class))); + assertTrue(isSimpleType(getTypeElement(BigDecimal.class))); + assertTrue(isSimpleType(getTypeElement(BigInteger.class))); + assertTrue(isSimpleType(getTypeElement(Date.class))); + assertTrue(isSimpleType(getTypeElement(Object.class))); + + assertFalse(isSimpleType(getTypeElement(getClass()))); + } + + @Test + void testIsSimpleTypeOnNull() { + assertFalse(isSimpleType(NULL_TYPE_ELEMENT)); + assertFalse(isSimpleType(NULL_TYPE_MIRROR)); + } + + @Test + void testIsSameType() { + assertIsSameType(testTypeElement, testClass); + + assertFalse(isSameType(getDeclaredType(String.class), "java.lang.Void")); + } + + @Test + void testIsSameTypeOnNull() { + assertFalse(isSameType(NULL_TYPE_MIRROR, testClass)); + assertFalse(isSameType(NULL_TYPE_MIRROR, testClassName)); + assertFalse(isSameType(NULL_ELEMENT, testClass)); + assertFalse(isSameType(NULL_ELEMENT, testClassName)); + + assertFalse(isSameType(testTypeElement, NULL_TYPE)); + assertFalse(isSameType(testTypeElement, NULL_STRING)); + + assertFalse(isSameType(testTypeMirror, NULL_TYPE)); + assertFalse(isSameType(testTypeMirror, NULL_STRING)); + + assertTrue(isSameType(NULL_TYPE_MIRROR, NULL_TYPE)); + assertTrue(isSameType(NULL_TYPE_MIRROR, NULL_STRING)); + assertTrue(isSameType(NULL_ELEMENT, NULL_TYPE)); + assertTrue(isSameType(NULL_ELEMENT, NULL_STRING)); + } + + @Test + void testIsArrayTypeOnTypeMirror() { + assertIsArrayType(ArrayTypeModel.class); + + assertFalse(isArrayType(getTypeMirror(Color.class))); + assertFalse(isArrayType(getTypeMirror(ArrayTypeModel.class))); + } + + @Test + void testIsArrayTypeOnElement() { + assertIsArrayType(getTypeElement(ArrayTypeModel.class)); + + assertFalse(isArrayType(getTypeElement(Color.class))); + assertFalse(isArrayType(getTypeElement(ArrayTypeModel.class))); + } + + @Test + void testIsArrayTypeOnNull() { + assertFalse(isArrayType(NULL_ELEMENT)); + assertFalse(isArrayType(NULL_TYPE_MIRROR)); + } + + @Test + void testIsEnumType() { + assertTrue(isEnumType(getDeclaredType(Color.class))); + assertFalse(isEnumType(getTypeElement(ArrayTypeModel.class))); + } + + @Test + void testIsEnumTypeOnNull() { + assertFalse(isEnumType(NULL_ELEMENT)); + assertFalse(isEnumType(NULL_TYPE_MIRROR)); + } + + @Test + void testIsClassType() { + // class + assertTrue(isClassType(getTypeElement(ArrayTypeModel.class))); + assertTrue(isClassType(getDeclaredType(ArrayTypeModel.class))); + + assertTrue(isClassType(getTypeElement(Model.class))); + assertTrue(isClassType(getDeclaredType(Model.class))); + + // enum + assertFalse(isClassType(getTypeElement(TimeUnit.class))); + assertFalse(isClassType(getDeclaredType(TimeUnit.class))); + + // interface + assertFalse(isClassType(getTypeElement(Serializable.class))); + assertFalse(isClassType(getDeclaredType(Serializable.class))); + } + + @Test + void testIsClassTypeOnNull() { + assertFalse(isClassType(NULL_ELEMENT)); + assertFalse(isClassType(NULL_TYPE_MIRROR)); + } + + @Test + void testIsPrimitiveType() { + TypeElement type = getTypeElement(PrimitiveTypeModel.class); + + getDeclaredFields(type).forEach(t -> { + assertTrue(isPrimitiveType(t)); + assertTrue(isPrimitiveType(t.asType())); + }); + + assertFalse(isPrimitiveType(getTypeElement(ArrayTypeModel.class))); + } + + @Test + void testIsPrimitiveTypeOnNull() { + assertFalse(isPrimitiveType(NULL_ELEMENT)); + assertFalse(isPrimitiveType(NULL_TYPE_MIRROR)); + } + + @Test + void testIsInterfaceType() { + assertTrue(isInterfaceType(getTypeElement(CharSequence.class))); + assertTrue(isInterfaceType(getDeclaredType(CharSequence.class))); + + assertFalse(isInterfaceType(getTypeElement(Model.class))); + assertFalse(isInterfaceType(getDeclaredType(Model.class))); + } + + @Test + void testIsInterfaceTypeOnNull() { + assertFalse(isInterfaceType(NULL_ELEMENT)); + assertFalse(isInterfaceType(NULL_TYPE_MIRROR)); + } + + @Test + void testIsAnnotationType() { + assertTrue(isAnnotationType(getTypeElement(Override.class))); + assertTrue(isAnnotationType(getDeclaredType(Override.class))); + + assertFalse(isAnnotationType(getTypeElement(Model.class))); + assertFalse(isAnnotationType(getDeclaredType(Model.class))); + } + + @Test + void testIsAnnotationTypeOnNull() { + assertFalse(isAnnotationType(NULL_ELEMENT)); + assertFalse(isAnnotationType(NULL_TYPE_MIRROR)); + } + + @Test + void testIsTypeElement() { + assertTrue(isTypeElement(testTypeElement)); + assertTrue(isTypeElement(testTypeMirror)); + assertTrue(isTypeElement(getFieldType(testTypeElement, "context"))); + + // primitive type + assertFalse(isTypeElement(getTypeMirror(int.class))); + } + + @Test + void testIsTypeElementOnNull() { + assertFalse(isTypeElement(NULL_ELEMENT)); + assertFalse(isTypeElement(NULL_TYPE_MIRROR)); + } + + @Test + void testIsDeclaredType() { + assertTrue(isDeclaredType(testTypeElement)); + assertTrue(isDeclaredType(testTypeMirror)); + assertFalse(isDeclaredType(types.getNullType())); + assertFalse(isDeclaredType(types.getPrimitiveType(TypeKind.BYTE))); + assertFalse(isDeclaredType(types.getArrayType(types.getPrimitiveType(TypeKind.BYTE)))); + + // field + assertFalse(isDeclaredType(findField(getTypeMirror(PrimitiveTypeModel.class), "z"))); + + // method + assertFalse(isDeclaredType(findMethod(testTypeElement, "close"))); + } + + @Test + void testIsDeclaredTypeOnNull() { + assertFalse(isDeclaredType(NULL_ELEMENT)); + assertFalse(isDeclaredType(NULL_TYPE_MIRROR)); + } + + @Test + void testOfTypeElement() { + assertEquals(testTypeElement, ofTypeElement(testTypeElement)); + assertEquals(testTypeElement, ofTypeElement(testTypeMirror)); + } + + @Test + void testOfTypeElementOnNull() { + assertNull(ofTypeElement(NULL_ELEMENT)); + assertNull(ofTypeElement(NULL_TYPE_MIRROR)); + } + + @Test + void testOfDeclaredType() { + assertEquals(testTypeMirror, testDeclaredType); + assertEquals(testTypeMirror, ofDeclaredType(testTypeMirror)); + assertEquals(testDeclaredType, ofDeclaredType(testTypeMirror)); + } + + @Test + void testOfDeclaredTypeOnNull() { + assertNull(ofDeclaredType(NULL_ELEMENT)); + assertNull(ofDeclaredType(NULL_TYPE_MIRROR)); + } + + @Test + void testOfTypeMirrors() { + assertOfTypeMirrors(String.class, SELF_TYPE, Color.class); + } + + @Test + void testOfTypeMirrorsOnNull() { + assertEmptyList(ofTypeMirrors(EMPTY_ELEMENT_ARRAY)); + assertEmptyList(ofTypeMirrors(NULL_COLLECTION)); + } + + @Test + void testOfTypeMirrorsOnEmpty() { + assertEmptyList(ofTypeMirrors(EMPTY_ELEMENT_ARRAY)); + assertEmptyList(ofTypeMirrors(emptyList())); + } + + @Test + void testOfTypeElements() { + assertOfTypeElements(String.class, SELF_TYPE, Color.class); + } + + @Test + void testOfTypeElementsOnNull() { + assertEmptyList(ofTypeElements(NULL_TYPE_MIRROR_ARRAY)); + assertEmptyList(ofTypeElements(NULL_COLLECTION)); + } + + @Test + void testOfTypeElementsOnEmpty() { + assertEmptyList(ofTypeElements(EMPTY_TYPE_MIRROR_ARRAY)); + assertEmptyList(ofTypeElements(emptyList())); + } + + @Test + void testOfDeclaredTypes() { + assertOfDeclaredTypes(String.class, SELF_TYPE, Color.class); + } + + @Test + void testOfDeclaredTypesWithFilter() { + List declaredTypes = ofDeclaredTypes(ofList(getTypeElement(String.class), getTypeElement(TestServiceImpl.class), getTypeElement(Color.class)), t -> true); + assertDeclaredTypes(declaredTypes, String.class, SELF_TYPE, Color.class); + } + + @Test + void testOfDeclaredTypesOnNull() { + assertEmptyList(ofDeclaredTypes(NULL_ELEMENT_ARRAY)); + assertEmptyList(ofDeclaredTypes(NULL_COLLECTION)); + } + + @Test + void testOfDeclaredTypesOnEmpty() { + assertEmptyList(ofDeclaredTypes(emptyList())); + } + + @Test + void testGetTypeElementOfSuperclass() { + TypeElement superTypeElement = getTypeElementOfSuperclass(testTypeElement); + assertEquals(getTypeElement(GenericTestService.class), superTypeElement); + + superTypeElement = getTypeElementOfSuperclass(superTypeElement); + assertEquals(getTypeElement(DefaultTestService.class), superTypeElement); + + superTypeElement = getTypeElementOfSuperclass(superTypeElement); + assertEquals(getTypeElement(Object.class), superTypeElement); + + assertNull(getTypeElementOfSuperclass(superTypeElement)); + } + + @Test + void testGetTypeElementOfSuperclassOnNull() { + assertNull(getTypeElementOfSuperclass(NULL_TYPE_ELEMENT)); + } + + @Test + void testGetAllTypeElementsOfSuperclasses() { + List allSuperTypes = getAllTypeElementsOfSuperclasses(testTypeElement); + assertTypeElements(allSuperTypes, ALL_SUPER_CLASSES); + } + + @Test + void testGetAllTypeElementsOfSuperclassesOnNull() { + assertEmptyList(getAllTypeElementsOfSuperclasses(NULL_TYPE_ELEMENT)); + } + + @Test + void testGetTypeElementsOfInterfaces() { + List typeElements = getTypeElementsOfInterfaces(testTypeElement); + assertTypeElements(typeElements, SUPER_INTERFACES); + } + + @Test + void testGetTypeElementsOfInterfacesOnNull() { + assertEmptyList(getTypeElementsOfInterfaces(NULL_TYPE_ELEMENT)); + } + + @Test + void testGetAllTypeElementsOfInterfaces() { + List typeElements = getAllTypeElementsOfInterfaces(testTypeElement); + assertTypeElements(typeElements, ALL_SUPER_INTERFACES); + } + + @Test + void testGetAllTypeElementsOfInterfacesOnNull() { + assertEmptyList(getAllTypeElementsOfInterfaces(NULL_TYPE_ELEMENT)); + } + + @Test + void testGetAllTypeElements() { + List allTypeElements = getAllTypeElements(testTypeElement); + assertTypeElements(allTypeElements, ALL_TYPES); + } + + @Test + void testGetAllTypeElementsOnNull() { + assertEmptyList(getAllTypeElements(NULL_TYPE_ELEMENT)); + } + + @Test + void testGetTypeElementsWithNoArgument() { + List typeElements = TypeUtils.getTypeElements(testTypeElement); + assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testGetTypeElements() { + // true true true true : all types + List typeElements = TypeUtils.getTypeElements(testTypeElement, true, true, true, true); + assertTypeElements(typeElements, ALL_TYPES); + assertEquals(getAllTypeElements(testTypeElement), typeElements); + + // true true true false : self type + all super classes + typeElements = TypeUtils.getTypeElements(testTypeElement, true, true, true, false); + assertTypeElements(typeElements, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + + // true true false true : self type + all super interfaces + typeElements = TypeUtils.getTypeElements(testTypeElement, true, true, false, true); + assertTypeElements(typeElements, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + + // true true false false : self type + typeElements = TypeUtils.getTypeElements(testTypeElement, true, true, false, false); + assertTypeElements(typeElements, SELF_TYPE); + + // true false true true : self type + super class + super interfaces + typeElements = TypeUtils.getTypeElements(testTypeElement, true, false, true, true); + assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + + // true false true false : self type + super class + typeElements = TypeUtils.getTypeElements(testTypeElement, true, false, true, false); + assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS); + + // true false false true : self type + super interfaces + typeElements = TypeUtils.getTypeElements(testTypeElement, true, false, false, true); + assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_INTERFACES); + + // true false false false : self type + typeElements = TypeUtils.getTypeElements(testTypeElement, true, false, false, false); + assertTypeElements(typeElements, SELF_TYPE); + + // false true true true : all super types + typeElements = TypeUtils.getTypeElements(testTypeElement, false, true, true, true); + assertTypeElements(typeElements, ALL_SUPER_TYPES); + assertEquals(getAllTypeElementsOfSuperTypes(testTypeElement), typeElements); + + // false true true false : all super classes + typeElements = TypeUtils.getTypeElements(testTypeElement, false, true, true, false); + assertTypeElements(typeElements, ALL_SUPER_CLASSES); + assertEquals(getAllTypeElementsOfSuperclasses(testTypeElement), typeElements); + + // false true false true : all super interfaces + typeElements = TypeUtils.getTypeElements(testTypeElement, false, true, false, true); + assertTypeElements(typeElements, ALL_SUPER_INTERFACES); + assertEquals(getAllTypeElementsOfInterfaces(testTypeElement), typeElements); + + // false true false false : nothing + typeElements = TypeUtils.getTypeElements(testTypeElement, false, true, false, false); + assertTypeElements(typeElements); + assertEmptyList(typeElements); + + // false false true true : super class + super interfaces + typeElements = TypeUtils.getTypeElements(testTypeElement, false, false, true, true); + assertTypeElements(typeElements, SUPER_TYPES); + + // false false true false : super class + typeElements = TypeUtils.getTypeElements(testTypeElement, false, false, true, false); + assertTypeElements(typeElements, SUPER_CLASS); + assertEquals(ofList(getTypeElementOfSuperclass(testTypeElement)), typeElements); + + // false false false true : super interfaces + typeElements = TypeUtils.getTypeElements(testTypeElement, false, false, false, true); + assertTypeElements(typeElements, SUPER_INTERFACES); + assertEquals(ofList(getTypeElementsOfInterfaces(testTypeElement)), typeElements); + + // false false false false : nothing + typeElements = TypeUtils.getTypeElements(testTypeElement, false, false, false, false); + assertTypeElements(typeElements); + assertEmptyList(typeElements); + } + + @Test + void testGetTypeElementsOnNull() { + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, true, true, true)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, true, true, false)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, true, false, true)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, true, false, false)); + + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, true, false, true, true)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, true, false)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, false, true)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, false, false)); + + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, true, true, true)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, true, true, false)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, true, false, true)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, true, false, false)); + + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, true, true)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, true, false)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, false, true)); + assertEmptyList(TypeUtils.getTypeElements(NULL_TYPE_ELEMENT, false, false, false, false)); + } + + @Test + void testFindAllTypeElementsOfSuperclasses() { + List typeElements = findAllTypeElementsOfSuperclasses(testTypeElement, alwaysTrue()); + assertTypeElements(typeElements, ALL_SUPER_CLASSES); + assertEquals(getAllTypeElementsOfSuperclasses(testTypeElement), typeElements); + + assertEmptyList(findAllTypeElementsOfSuperclasses(testTypeElement, alwaysFalse())); + } + + @Test + void testFindAllTypeElementsOfSuperclassesOnNull() { + assertEmptyList(findAllTypeElementsOfSuperclasses(NULL_TYPE_ELEMENT, alwaysTrue())); + assertEmptyList(findAllTypeElementsOfSuperclasses(NULL_TYPE_ELEMENT, alwaysFalse())); + } + + @Test + void testFindAllTypeElementsOfInterfaces() { + List typeElements = findAllTypeElementsOfInterfaces(testTypeElement, alwaysTrue()); + assertTypeElements(typeElements, ALL_SUPER_INTERFACES); + assertEquals(getAllTypeElementsOfInterfaces(testTypeElement), typeElements); + + assertEmptyList(findAllTypeElementsOfInterfaces(testTypeElement, alwaysFalse())); + } + + @Test + void testFindAllTypeElementsOfInterfacesOnNull() { + assertEmptyList(findAllTypeElementsOfInterfaces(NULL_TYPE_ELEMENT, alwaysTrue())); + assertEmptyList(findAllTypeElementsOfInterfaces(NULL_TYPE_ELEMENT, alwaysFalse())); + } + + @Test + void testFindTypeElementsOfInterfaces() { + List typeElements = findTypeElementsOfInterfaces(testTypeElement, alwaysTrue()); + assertTypeElements(typeElements, SUPER_INTERFACES); + assertEquals(getTypeElementsOfInterfaces(testTypeElement), typeElements); + + assertEmptyList(findTypeElementsOfInterfaces(testTypeElement, alwaysFalse())); + } + + @Test + void testFindTypeElementsOfInterfacesOnNull() { + assertEmptyList(findTypeElementsOfInterfaces(NULL_TYPE_ELEMENT, alwaysTrue())); + assertEmptyList(findTypeElementsOfInterfaces(NULL_TYPE_ELEMENT, alwaysFalse())); + } + + @Test + void testFindTypeElements() { + // true true true true : all types + List typeElements = findTypeElements(testTypeElement, true, true, true, true, alwaysTrue()); + assertTypeElements(typeElements, ALL_TYPES); + assertEquals(getAllTypeElements(testTypeElement), typeElements); + + // true true true false : self type + all super classes + typeElements = findTypeElements(testTypeElement, true, true, true, false, alwaysTrue()); + assertTypeElements(typeElements, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + + // true true false true : self type + all super interfaces + typeElements = findTypeElements(testTypeElement, true, true, false, true, alwaysTrue()); + assertTypeElements(typeElements, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + + // true true false false : self type + typeElements = findTypeElements(testTypeElement, true, true, false, false, alwaysTrue()); + assertTypeElements(typeElements, SELF_TYPE); + + // true false true true : self type + super class + super interfaces + typeElements = findTypeElements(testTypeElement, true, false, true, true, alwaysTrue()); + assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + + // true false true false : self type + super class + typeElements = findTypeElements(testTypeElement, true, false, true, false, alwaysTrue()); + assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_CLASS); + + // true false false true : self type + super interfaces + typeElements = findTypeElements(testTypeElement, true, false, false, true, alwaysTrue()); + assertTypeElements(typeElements, SELF_TYPE_PLUS_SUPER_INTERFACES); + + // true false false false : self type + typeElements = findTypeElements(testTypeElement, true, false, false, false, alwaysTrue()); + assertTypeElements(typeElements, SELF_TYPE); + + // false true true true : all super types + typeElements = findTypeElements(testTypeElement, false, true, true, true, alwaysTrue()); + assertTypeElements(typeElements, ALL_SUPER_TYPES); + assertEquals(getAllTypeElementsOfSuperTypes(testTypeElement), typeElements); + + // false true true false : all super classes + typeElements = findTypeElements(testTypeElement, false, true, true, false, alwaysTrue()); + assertTypeElements(typeElements, ALL_SUPER_CLASSES); + assertEquals(getAllTypeElementsOfSuperclasses(testTypeElement), typeElements); + + // false true false true : all super interfaces + typeElements = findTypeElements(testTypeElement, false, true, false, true, alwaysTrue()); + assertTypeElements(typeElements, ALL_SUPER_INTERFACES); + assertEquals(getAllTypeElementsOfInterfaces(testTypeElement), typeElements); + + // false true false false : nothing + typeElements = findTypeElements(testTypeElement, false, true, false, false, alwaysTrue()); + assertTypeElements(typeElements); + assertEmptyList(typeElements); + + // false false true true : super types + typeElements = findTypeElements(testTypeElement, false, false, true, true, alwaysTrue()); + assertTypeElements(typeElements, SUPER_TYPES); + + // false false true false : super class + typeElements = findTypeElements(testTypeElement, false, false, true, false, alwaysTrue()); + assertTypeElements(typeElements, SUPER_CLASS); + assertEquals(ofList(getTypeElementOfSuperclass(testTypeElement)), typeElements); + + // false false false true : super interfaces + typeElements = findTypeElements(testTypeElement, false, false, false, true, alwaysTrue()); + assertTypeElements(typeElements, SUPER_INTERFACES); + assertEquals(ofList(getTypeElementsOfInterfaces(testTypeElement)), typeElements); + + // false false false false : nothing + typeElements = findTypeElements(testTypeElement, false, false, false, false, alwaysTrue()); + assertTypeElements(typeElements); + assertEmptyList(typeElements); + } + + @Test + void testFindTypeElementsOnNullFilterElement() { + assertThrows(IllegalArgumentException.class, + () -> findTypeElements(testTypeElement, true, true, true, true, new Predicate[]{null})); + } + + + @Test + void testGetDeclaredTypeOfSuperclass() { + DeclaredType superDeclaredType = getDeclaredTypeOfSuperclass(testTypeMirror); + assertEquals(superDeclaredType, getTypeElement(GenericTestService.class).asType()); + + superDeclaredType = getDeclaredTypeOfSuperclass(superDeclaredType); + assertEquals(superDeclaredType, getTypeElement(DefaultTestService.class).asType()); + + superDeclaredType = getDeclaredTypeOfSuperclass(superDeclaredType); + assertEquals(superDeclaredType, getTypeElement(Object.class).asType()); + + assertNull(getDeclaredTypeOfSuperclass(superDeclaredType)); + } + + @Test + void testGetDeclaredTypeOfSuperclassOnNull() { + assertNull(getDeclaredTypeOfSuperclass(NULL_ELEMENT)); + assertNull(getDeclaredTypeOfSuperclass(NULL_TYPE_MIRROR)); + } + + @Test + void testGetDeclaredTypesOfInterfaces() { + List declaredTypes = getDeclaredTypesOfInterfaces(testTypeMirror); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + } + + @Test + void testGetDeclaredTypesOfInterfacesOnNull() { + assertEmptyList(getDeclaredTypesOfInterfaces(NULL_ELEMENT)); + assertEmptyList(getDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR)); + } + + @Test + void testGetAllDeclaredTypesOfSuperclasses() { + List declaredTypes = getAllDeclaredTypesOfSuperclasses(testTypeMirror); + assertDeclaredTypes(declaredTypes, ALL_SUPER_CLASSES); + } + + @Test + void testGetAllDeclaredTypesOfSuperclassesOnNull() { + assertEmptyList(getAllDeclaredTypesOfSuperclasses(NULL_ELEMENT)); + assertEmptyList(getAllDeclaredTypesOfSuperclasses(NULL_TYPE_MIRROR)); + } + + @Test + void testGetAllDeclaredTypesOfInterfaces() { + List declaredTypes = getAllDeclaredTypesOfInterfaces(testTypeMirror); + assertDeclaredTypes(declaredTypes, ALL_SUPER_INTERFACES); + } + + @Test + void testGetAllDeclaredTypesOfInterfacesOnNull() { + assertEmptyList(getAllDeclaredTypesOfInterfaces(NULL_ELEMENT)); + assertEmptyList(getAllDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR)); + } + + @Test + void testGetAllDeclaredTypesOfSuperTypes() { + List declaredTypes = getAllDeclaredTypesOfSuperTypes(testTypeMirror); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + } + + @Test + void testGetAllDeclaredTypesOfSuperTypesOnNull() { + assertEmptyList(getAllDeclaredTypesOfSuperTypes(NULL_ELEMENT)); + assertEmptyList(getAllDeclaredTypesOfSuperTypes(NULL_TYPE_MIRROR)); + } + + @Test + void testGetAllDeclaredTypes() { + List declaredTypes = getAllDeclaredTypes(testTypeMirror); + assertDeclaredTypes(declaredTypes, ALL_TYPES); + } + + @Test + void testGetAllDeclaredTypesOnNull() { + assertEmptyList(getAllDeclaredTypes(NULL_ELEMENT)); + assertEmptyList(getAllDeclaredTypes(NULL_TYPE_MIRROR)); + } + + @Test + void testGetDeclaredTypes() { + // true true true true : all types + List declaredTypes = getDeclaredTypes(testTypeElement, true, true, true, true); + assertDeclaredTypes(declaredTypes, ALL_TYPES); + assertEquals(getAllDeclaredTypes(testTypeElement), declaredTypes); + + // true true true false : self type + all super classes + declaredTypes = getDeclaredTypes(testTypeElement, true, true, true, false); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + + // true true false true : self type + all super interfaces + declaredTypes = getDeclaredTypes(testTypeElement, true, true, false, true); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + + // true true false false : self type + declaredTypes = getDeclaredTypes(testTypeElement, true, true, false, false); + assertDeclaredTypes(declaredTypes, SELF_TYPE); + + // true false true true : self type + super class + super interfaces + declaredTypes = getDeclaredTypes(testTypeElement, true, false, true, true); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + + // true false true false : self type + super class + declaredTypes = getDeclaredTypes(testTypeElement, true, false, true, false); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_CLASS); + + // true false false true : self type + super interfaces + declaredTypes = getDeclaredTypes(testTypeElement, true, false, false, true); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_INTERFACES); + + // true false false false : self type + declaredTypes = getDeclaredTypes(testTypeElement, true, false, false, false); + assertDeclaredTypes(declaredTypes, SELF_TYPE); + + // false true true true : all super types + declaredTypes = getDeclaredTypes(testTypeElement, false, true, true, true); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + assertEquals(getAllDeclaredTypesOfSuperTypes(testTypeElement), declaredTypes); + + // false true true false : all super classes + declaredTypes = getDeclaredTypes(testTypeElement, false, true, true, false); + assertDeclaredTypes(declaredTypes, ALL_SUPER_CLASSES); + assertEquals(getAllDeclaredTypesOfSuperclasses(testTypeElement), declaredTypes); + + // false true false true : all super interfaces + declaredTypes = getDeclaredTypes(testTypeElement, false, true, false, true); + assertDeclaredTypes(declaredTypes, ALL_SUPER_INTERFACES); + assertEquals(getAllDeclaredTypesOfInterfaces(testTypeElement), declaredTypes); + + // false true false false : nothing + declaredTypes = getDeclaredTypes(testTypeElement, false, true, false, false); + assertDeclaredTypes(declaredTypes); + assertEmptyList(declaredTypes); + + // false false true true : super class + super interfaces + declaredTypes = getDeclaredTypes(testTypeElement, false, false, true, true); + assertDeclaredTypes(declaredTypes, SUPER_TYPES); + + // false false true false : super class + declaredTypes = getDeclaredTypes(testTypeElement, false, false, true, false); + assertDeclaredTypes(declaredTypes, SUPER_CLASS); + assertEquals(ofList(getDeclaredTypeOfSuperclass(testTypeElement)), declaredTypes); + + // false false false true : super interfaces + declaredTypes = getDeclaredTypes(testTypeElement, false, false, false, true); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + assertEquals(ofList(getDeclaredTypesOfInterfaces(testTypeElement)), declaredTypes); + + // false false false false : nothing + declaredTypes = getDeclaredTypes(testTypeElement, false, false, false, false); + assertDeclaredTypes(declaredTypes); + assertEmptyList(declaredTypes); + } + + @Test + void testFindDeclaredTypesWithExcludedTypes() { + List declaredTypes = findDeclaredTypes(testTypeElement, SUPER_CLASS); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + + declaredTypes = findDeclaredTypes(testTypeElement, getTypeNames(SUPER_CLASS)); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + + declaredTypes = findDeclaredTypes(testTypeElement, SUPER_INTERFACES); + assertDeclaredTypes(declaredTypes, SUPER_CLASS); + + declaredTypes = findDeclaredTypes(testTypeElement, getTypeNames(SUPER_INTERFACES)); + assertDeclaredTypes(declaredTypes, SUPER_CLASS); + + declaredTypes = findDeclaredTypes(testTypeMirror, SUPER_CLASS); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + + declaredTypes = findDeclaredTypes(testTypeMirror, getTypeNames(SUPER_CLASS)); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + + declaredTypes = findDeclaredTypes(testTypeMirror, SUPER_INTERFACES); + assertDeclaredTypes(declaredTypes, SUPER_CLASS); + + declaredTypes = findDeclaredTypes(testTypeMirror, getTypeNames(SUPER_INTERFACES)); + assertDeclaredTypes(declaredTypes, SUPER_CLASS); + } + + @Test + void testFindDeclaredTypesWithExcludedTypesOnNull() { + assertEmptyList(findDeclaredTypes(NULL_ELEMENT, NULL_TYPE_ARRAY)); + assertEmptyList(findDeclaredTypes(NULL_ELEMENT, EMPTY_TYPE_ARRAY)); + assertEmptyList(findDeclaredTypes(NULL_ELEMENT, ALL_TYPES)); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, NULL_TYPE_ARRAY)); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, EMPTY_TYPE_ARRAY)); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, ALL_TYPES)); + } + + @Test + void testFindDeclaredTypesWithExcludedTypeNamesOnNull() { + assertEmptyList(findDeclaredTypes(NULL_ELEMENT, NULL_STRING_ARRAY)); + assertEmptyList(findDeclaredTypes(NULL_ELEMENT, EMPTY_STRING_ARRAY)); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, NULL_STRING_ARRAY)); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, EMPTY_STRING_ARRAY)); + } + + @Test + void testFindDeclaredTypesOfInterfaces() { + List declaredTypes = findDeclaredTypesOfInterfaces(testTypeMirror, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + + findDeclaredTypesOfInterfaces(testTypeElement, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + + declaredTypes = findDeclaredTypesOfInterfaces(testTypeMirror, alwaysFalse()); + assertEmptyList(declaredTypes); + + declaredTypes = findDeclaredTypesOfInterfaces(testTypeElement, alwaysFalse()); + assertEmptyList(declaredTypes); + } + + @Test + void testFindDeclaredTypesOfInterfacesOnNull() { + assertEmptyList(findDeclaredTypesOfInterfaces(NULL_ELEMENT, alwaysTrue())); + assertEmptyList(findDeclaredTypesOfInterfaces(NULL_ELEMENT, alwaysFalse())); + assertEmptyList(findDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue())); + assertEmptyList(findDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue())); + } + + @Test + void testFindAllDeclaredTypesOfSuperclasses() { + List declaredTypes = findAllDeclaredTypesOfSuperclasses(testTypeElement, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_SUPER_CLASSES); + + declaredTypes = findAllDeclaredTypesOfSuperclasses(testTypeMirror, alwaysFalse()); + assertEmptyList(declaredTypes); + } + + @Test + void testFindAllDeclaredTypesOfSuperclassesOnNull() { + assertEmptyList(findAllDeclaredTypesOfSuperclasses(NULL_ELEMENT, alwaysTrue())); + assertEmptyList(findAllDeclaredTypesOfSuperclasses(NULL_ELEMENT, alwaysFalse())); + assertEmptyList(findAllDeclaredTypesOfSuperclasses(NULL_TYPE_MIRROR, alwaysTrue())); + assertEmptyList(findAllDeclaredTypesOfSuperclasses(NULL_TYPE_MIRROR, alwaysFalse())); + } + + @Test + void testFindAllDeclaredTypesOfInterfaces() { + List declaredTypes = findAllDeclaredTypesOfInterfaces(testTypeElement, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_SUPER_INTERFACES); + + declaredTypes = findAllDeclaredTypesOfInterfaces(testTypeMirror, alwaysFalse()); + assertEmptyList(declaredTypes); + } + + @Test + void testFindAllDeclaredTypesOfInterfacesOnNull() { + assertEmptyList(findAllDeclaredTypesOfInterfaces(NULL_ELEMENT, alwaysTrue())); + assertEmptyList(findAllDeclaredTypesOfInterfaces(NULL_ELEMENT, alwaysFalse())); + assertEmptyList(findAllDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue())); + assertEmptyList(findAllDeclaredTypesOfInterfaces(NULL_TYPE_MIRROR, alwaysFalse())); + } + + @Test + void testFindAllDeclaredTypesOfSuperTypes() { + List declaredTypes = findAllDeclaredTypesOfSuperTypes(testTypeMirror, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + + findAllDeclaredTypesOfSuperTypes(testTypeElement, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + + declaredTypes = findAllDeclaredTypesOfSuperTypes(testTypeMirror, alwaysFalse()); + assertEmptyList(declaredTypes); + + declaredTypes = findAllDeclaredTypesOfSuperTypes(testTypeElement, alwaysFalse()); + assertEmptyList(declaredTypes); + } + + @Test + void testFindAllDeclaredTypesOfSuperTypesOnNull() { + assertEmptyList(findAllDeclaredTypesOfSuperTypes(NULL_ELEMENT)); + assertEmptyList(findAllDeclaredTypesOfSuperTypes(NULL_TYPE_MIRROR)); + } + + @Test + void testFindAllDeclaredTypes() { + List declaredTypes = findAllDeclaredTypes(testTypeElement, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_TYPES); + + declaredTypes = findAllDeclaredTypes(testTypeMirror, alwaysFalse()); + assertEmptyList(declaredTypes); + } + + @Test + void testFindAllDeclaredTypesOnNull() { + assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, alwaysTrue())); + assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, alwaysFalse())); + assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, alwaysTrue())); + assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, alwaysFalse())); + } + + @Test + void testFindAllDeclaredTypesWithExcludedTypes() { + List declaredTypes = findAllDeclaredTypes(testTypeElement, testClass); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + + + declaredTypes = findAllDeclaredTypes(testTypeMirror, testClass); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + } + + @Test + void testFindAllDeclaredTypesWithExcludedTypeNames() { + List declaredTypes = findAllDeclaredTypes(testTypeElement, testClassName); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + + declaredTypes = findAllDeclaredTypes(testTypeMirror, testClassName); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + } + + @Test + void testFindAllDeclaredTypesWithExcludedTypesOnNull() { + assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, NULL_TYPE_ARRAY)); + assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, EMPTY_TYPE_ARRAY)); + assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, ALL_TYPES)); + assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, NULL_TYPE_ARRAY)); + assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, EMPTY_TYPE_ARRAY)); + assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, ALL_TYPES)); + } + + @Test + void testFindAllDeclaredTypesWithExcludedTypeNamesOnNull() { + assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, NULL_STRING_ARRAY)); + assertEmptyList(findAllDeclaredTypes(NULL_ELEMENT, EMPTY_STRING_ARRAY)); + assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, NULL_STRING_ARRAY)); + assertEmptyList(findAllDeclaredTypes(NULL_TYPE_MIRROR, EMPTY_STRING_ARRAY)); + } + + @Test + void testFindDeclaredTypes() { + // true true true true : all types + List declaredTypes = findDeclaredTypes(testTypeElement, true, true, true, true, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_TYPES); + assertEquals(getAllDeclaredTypes(testTypeElement), declaredTypes); + + // true true true false : self type + all super classes + declaredTypes = findDeclaredTypes(testTypeElement, true, true, true, false, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + + // true true false true : self type + all super interfaces + declaredTypes = findDeclaredTypes(testTypeElement, true, true, false, true, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + + // true true false false : self type + declaredTypes = findDeclaredTypes(testTypeElement, true, true, false, false, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SELF_TYPE); + + // true false true true : self type + super class + super interfaces + declaredTypes = findDeclaredTypes(testTypeElement, true, false, true, true, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + + // true false true false : self type + super class + declaredTypes = findDeclaredTypes(testTypeElement, true, false, true, false, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_CLASS); + + // true false false true : self type + super interfaces + declaredTypes = findDeclaredTypes(testTypeElement, true, false, false, true, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SELF_TYPE_PLUS_SUPER_INTERFACES); + + // true false false false : self type + declaredTypes = findDeclaredTypes(testTypeElement, true, false, false, false, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SELF_TYPE); + + // false true true true : all super types + declaredTypes = findDeclaredTypes(testTypeElement, false, true, true, true, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_SUPER_TYPES); + assertEquals(getAllDeclaredTypesOfSuperTypes(testTypeElement), declaredTypes); + + // false true true false : all super classes + declaredTypes = findDeclaredTypes(testTypeElement, false, true, true, false, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_SUPER_CLASSES); + assertEquals(getAllDeclaredTypesOfSuperclasses(testTypeElement), declaredTypes); + + // false true false true : all super interfaces + declaredTypes = findDeclaredTypes(testTypeElement, false, true, false, true, alwaysTrue()); + assertDeclaredTypes(declaredTypes, ALL_SUPER_INTERFACES); + assertEquals(getAllDeclaredTypesOfInterfaces(testTypeElement), declaredTypes); + + // false true false false : nothing + declaredTypes = findDeclaredTypes(testTypeElement, false, true, false, false, alwaysTrue()); + assertDeclaredTypes(declaredTypes); + assertEmptyList(declaredTypes); + + // false false true true : super class + super interfaces + declaredTypes = findDeclaredTypes(testTypeElement, false, false, true, true, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SUPER_TYPES); + + // false false true false : super class + declaredTypes = findDeclaredTypes(testTypeElement, false, false, true, false, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SUPER_CLASS); + assertEquals(ofList(getDeclaredTypeOfSuperclass(testTypeElement)), declaredTypes); + + // false false false true : super interfaces + declaredTypes = findDeclaredTypes(testTypeElement, false, false, false, true, alwaysTrue()); + assertDeclaredTypes(declaredTypes, SUPER_INTERFACES); + assertEquals(ofList(getDeclaredTypesOfInterfaces(testTypeElement)), declaredTypes); + + // false false false false : nothing + declaredTypes = findDeclaredTypes(testTypeElement, false, false, false, false, alwaysTrue()); + assertDeclaredTypes(declaredTypes); + assertEmptyList(declaredTypes); + } + + @Test + void testFindDeclaredTypesOnNull() { + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, true, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, true, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, true, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, true, true, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, true, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, true, false, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, true, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, true, false, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, false, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, false, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, false, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, false, true, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, false, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, true, false, false, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, false, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, true, false, false, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, false, true, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, true, false, true, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, false, true, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, true, false, true, true, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, false, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, false, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, true, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, false, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, false, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, true, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, true, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, true, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, true, true, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, true, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, true, false, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, true, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, true, false, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, false, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, false, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, false, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, false, true, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, false, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, true, false, false, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, false, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, true, false, false, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, true, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, true, false, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, true, false, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, true, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, true, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, true, alwaysFalse())); + + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_ELEMENT, false, false, false, false, alwaysFalse())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, false, alwaysTrue())); + assertEmptyList(findDeclaredTypes(NULL_TYPE_MIRROR, false, false, false, false, alwaysFalse())); + } + + @Test + void testGetTypeMirrorsOfInterfaces() { + List typeMirrors = getTypeMirrorsOfInterfaces(testTypeMirror); + assertTypeMirrors(typeMirrors, SUPER_INTERFACES); + + typeMirrors = getTypeMirrorsOfInterfaces(testTypeElement); + assertTypeMirrors(typeMirrors, SUPER_INTERFACES); + + typeMirrors = getTypeMirrorsOfInterfaces(getTypeElement(Object.class)); + assertEmptyList(typeMirrors); + + typeMirrors = getTypeMirrorsOfInterfaces(getTypeMirror(Object.class)); + assertSame(typeMirrors, typeMirrors); + } + + @Test + void testGetTypeMirrorsOfInterfacesOnNull() { + List typeMirrors = getTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR); + assertEmptyList(typeMirrors); + + typeMirrors = getTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT); + assertEmptyList(typeMirrors); + } + + @Test + void testFindTypeMirrorsOfInterfaces() { + List typeMirrors = findTypeMirrorsOfInterfaces(testTypeMirror, alwaysTrue()); + assertTypeMirrors(typeMirrors, SUPER_INTERFACES); + + typeMirrors = findTypeMirrorsOfInterfaces(testTypeElement, alwaysTrue()); + assertTypeMirrors(typeMirrors, SUPER_INTERFACES); + + typeMirrors = findTypeMirrorsOfInterfaces(testTypeMirror, alwaysFalse()); + assertEmptyList(typeMirrors); + + typeMirrors = findTypeMirrorsOfInterfaces(testTypeElement, alwaysFalse()); + assertEmptyList(typeMirrors); + + typeMirrors = findTypeMirrorsOfInterfaces(getTypeElement(Object.class), alwaysTrue()); + assertEmptyList(typeMirrors); + + typeMirrors = findTypeMirrorsOfInterfaces(getTypeElement(Object.class), alwaysFalse()); + assertEmptyList(typeMirrors); + + typeMirrors = findTypeMirrorsOfInterfaces(getTypeMirror(Object.class), alwaysTrue()); + assertSame(typeMirrors, typeMirrors); + + typeMirrors = findTypeMirrorsOfInterfaces(getTypeMirror(Object.class), alwaysFalse()); + assertSame(typeMirrors, typeMirrors); + } + + @Test + void testFindTypeMirrorsOfInterfacesOnNull() { + List typeMirrors = findTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue()); + assertEmptyList(typeMirrors); + + typeMirrors = findTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT, alwaysTrue()); + assertEmptyList(typeMirrors); + + typeMirrors = findTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysFalse()); + assertEmptyList(typeMirrors); + + typeMirrors = findTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT, alwaysFalse()); + assertEmptyList(typeMirrors); + } + + @Test + void testGetAllTypeMirrorsOfInterfaces() { + List typeMirrors = getAllTypeMirrorsOfInterfaces(testTypeMirror); + assertTypeMirrors(typeMirrors, ALL_SUPER_INTERFACES); + + typeMirrors = getAllTypeMirrorsOfInterfaces(testTypeElement); + assertTypeMirrors(typeMirrors, ALL_SUPER_INTERFACES); + + typeMirrors = getAllTypeMirrorsOfInterfaces(getTypeElement(Object.class)); + assertEmptyList(typeMirrors); + + typeMirrors = getAllTypeMirrorsOfInterfaces(getTypeMirror(Object.class)); + assertSame(typeMirrors, typeMirrors); + } + + @Test + void testGetAllTypeMirrorsOfInterfacesOnNull() { + List typeMirrors = getAllTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR); + assertEmptyList(typeMirrors); + + typeMirrors = getAllTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT); + assertEmptyList(typeMirrors); + } + + @Test + void testFindAllTypeMirrorsOfInterfaces() { + List typeMirrors = findAllTypeMirrorsOfInterfaces(testTypeMirror, alwaysTrue()); + assertTypeMirrors(typeMirrors, ALL_SUPER_INTERFACES); + + typeMirrors = findAllTypeMirrorsOfInterfaces(testTypeElement, alwaysTrue()); + assertTypeMirrors(typeMirrors, ALL_SUPER_INTERFACES); + + typeMirrors = findAllTypeMirrorsOfInterfaces(testTypeMirror, alwaysFalse()); + assertEmptyList(typeMirrors); + + typeMirrors = findAllTypeMirrorsOfInterfaces(testTypeElement, alwaysFalse()); + assertEmptyList(typeMirrors); + + typeMirrors = findAllTypeMirrorsOfInterfaces(getTypeElement(Object.class), alwaysTrue()); + assertEmptyList(typeMirrors); + + typeMirrors = findAllTypeMirrorsOfInterfaces(getTypeElement(Object.class), alwaysFalse()); + assertEmptyList(typeMirrors); + + typeMirrors = findAllTypeMirrorsOfInterfaces(getTypeMirror(Object.class), alwaysTrue()); + assertSame(typeMirrors, typeMirrors); + + typeMirrors = findAllTypeMirrorsOfInterfaces(getTypeMirror(Object.class), alwaysFalse()); + assertSame(typeMirrors, typeMirrors); + } + + @Test + void testFindAllTypeMirrorsOfInterfacesOnNull() { + List typeMirrors = findAllTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysTrue()); + assertEmptyList(typeMirrors); + + typeMirrors = findAllTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysFalse()); + assertEmptyList(typeMirrors); + + typeMirrors = findAllTypeMirrorsOfInterfaces(NULL_TYPE_ELEMENT, alwaysTrue()); + assertEmptyList(typeMirrors); + + typeMirrors = findAllTypeMirrorsOfInterfaces(NULL_TYPE_MIRROR, alwaysFalse()); + assertEmptyList(typeMirrors); + } + + @Test + void testFindInterfaceTypeMirror() { + for (Type interfaceType : ALL_SUPER_INTERFACES) { + assertInterfaceTypeMirror(testTypeElement, interfaceType); + } + + for (Type superClass : ALL_SUPER_CLASSES) { + assertNull(findInterfaceTypeMirror(testTypeElement, superClass)); + assertNull(findInterfaceTypeMirror(testTypeMirror, superClass)); + } + } + + @Test + void testFindInterfaceTypeMirrorOnNull() { + for (Type type : ALL_TYPES) { + assertNull(findInterfaceTypeMirror(NULL_ELEMENT, type)); + assertNull(findInterfaceTypeMirror(NULL_TYPE_MIRROR, type)); + } + } + + @Test + void testGetTypeMirrors() { + assertGetTypeMirrors(NULL_TYPE); + assertGetTypeMirrors(SELF_TYPE); + assertGetTypeMirrors(SUPER_CLASS); + assertGetTypeMirrors(ALL_TYPES); + assertGetTypeMirrors(ALL_SUPER_TYPES); + assertGetTypeMirrors(ALL_SUPER_CLASSES); + assertGetTypeMirrors(ALL_SUPER_INTERFACES); + assertGetTypeMirrors(SUPER_INTERFACES); + assertGetTypeMirrors(SUPER_TYPES); + + assertGetTypeMirrors(SELF_TYPE_PLUS_ALL_SUPER_TYPES); + assertGetTypeMirrors(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + assertGetTypeMirrors(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + assertGetTypeMirrors(SELF_TYPE_PLUS_SUPER_CLASS); + assertGetTypeMirrors(SELF_TYPE_PLUS_SUPER_INTERFACES); + assertGetTypeMirrors(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testGetTypeMirrorsOnNull() { + assertGetTypeMirrors(NULL_TYPE); + assertGetTypeMirrors(NULL_TYPE_ARRAY); + assertGetTypeMirrors(EMPTY_TYPE_ARRAY); + + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SUPER_CLASS); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, ALL_TYPES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, ALL_SUPER_TYPES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, ALL_SUPER_CLASSES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, ALL_SUPER_INTERFACES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SUPER_INTERFACES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SUPER_TYPES); + + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_ALL_SUPER_TYPES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_SUPER_CLASS); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_SUPER_INTERFACES); + TypeUtils.getTypeMirrors(NULL_PROCESSING_ENVIRONMENT, SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testGetTypeMirror() { + assertGetTypeMirror(NULL_TYPE); + assertGetTypeMirror(SELF_TYPE); + assertGetTypeMirror(SUPER_CLASS); + assertGetTypeMirror(ALL_TYPES); + assertGetTypeMirror(ALL_SUPER_TYPES); + assertGetTypeMirror(ALL_SUPER_CLASSES); + assertGetTypeMirror(ALL_SUPER_INTERFACES); + assertGetTypeMirror(SUPER_INTERFACES); + assertGetTypeMirror(SUPER_TYPES); + + assertGetTypeMirror(SELF_TYPE_PLUS_ALL_SUPER_TYPES); + assertGetTypeMirror(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + assertGetTypeMirror(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + assertGetTypeMirror(SELF_TYPE_PLUS_SUPER_CLASS); + assertGetTypeMirror(SELF_TYPE_PLUS_SUPER_INTERFACES); + assertGetTypeMirror(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testGetTypeMirrorOnNull() { + assertNull(TypeUtils.getTypeMirror(this.processingEnv, NULL_TYPE)); + assertNull(getTypeMirror(NULL_TYPE)); + + assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE); + assertGetTypeMirrorOnNullProcessingEnvironment(SUPER_CLASS); + assertGetTypeMirrorOnNullProcessingEnvironment(ALL_TYPES); + assertGetTypeMirrorOnNullProcessingEnvironment(ALL_SUPER_TYPES); + assertGetTypeMirrorOnNullProcessingEnvironment(ALL_SUPER_CLASSES); + assertGetTypeMirrorOnNullProcessingEnvironment(ALL_SUPER_INTERFACES); + assertGetTypeMirrorOnNullProcessingEnvironment(SUPER_INTERFACES); + assertGetTypeMirrorOnNullProcessingEnvironment(SUPER_TYPES); + + assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_TYPES); + assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS); + assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_INTERFACES); + assertGetTypeMirrorOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testGetTypeElementsWithProcessingEnvironment() { + assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE); + assertGetTypeElementsWithProcessingEnvironment(SUPER_CLASS); + assertGetTypeElementsWithProcessingEnvironment(ALL_TYPES); + assertGetTypeElementsWithProcessingEnvironment(ALL_SUPER_TYPES); + assertGetTypeElementsWithProcessingEnvironment(ALL_SUPER_CLASSES); + assertGetTypeElementsWithProcessingEnvironment(ALL_SUPER_INTERFACES); + assertGetTypeElementsWithProcessingEnvironment(SUPER_INTERFACES); + assertGetTypeElementsWithProcessingEnvironment(SUPER_TYPES); + + assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_TYPES); + assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS); + assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_SUPER_INTERFACES); + assertGetTypeElementsWithProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testGetTypeElementsWithProcessingEnvironmentOnNull() { + assertEmptyList(TypeUtils.getTypeElements(this.processingEnv, NULL_TYPE)); + assertEmptyList(TypeUtils.getTypeElements(this.processingEnv, NULL_TYPE_ARRAY)); + assertEmptyList(TypeUtils.getTypeElements(this.processingEnv, EMPTY_TYPE_ARRAY)); + + assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE); + assertGetTypeElementsOnNullProcessingEnvironment(SUPER_CLASS); + assertGetTypeElementsOnNullProcessingEnvironment(ALL_TYPES); + assertGetTypeElementsOnNullProcessingEnvironment(ALL_SUPER_TYPES); + assertGetTypeElementsOnNullProcessingEnvironment(ALL_SUPER_CLASSES); + assertGetTypeElementsOnNullProcessingEnvironment(ALL_SUPER_INTERFACES); + assertGetTypeElementsOnNullProcessingEnvironment(SUPER_INTERFACES); + assertGetTypeElementsOnNullProcessingEnvironment(SUPER_TYPES); + + assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_TYPES); + assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS); + assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_INTERFACES); + assertGetTypeElementsOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testGetTypeElement() { + assertEquals(testTypeElement, TypeUtils.getTypeElement(processingEnv, testClassName)); + } + + @Test + void testGetTypeElementOnTypeMirror() { + assertEquals(testTypeElement, TypeUtils.getTypeElement(processingEnv, testTypeMirror)); + } + + @Test + void testGetTypeElementOnType() { + assertEquals(testTypeElement, TypeUtils.getTypeElement(processingEnv, SELF_TYPE)); + } + + @Test + void testGetTypeElementOnNull() { + assertNull(TypeUtils.getTypeElement(processingEnv, NULL_TYPE)); + assertNull(TypeUtils.getTypeElement(processingEnv, NULL_TYPE_MIRROR)); + assertNull(TypeUtils.getTypeElement(processingEnv, NULL_STRING)); + assertNull(TypeUtils.getTypeElement(NULL_PROCESSING_ENVIRONMENT, NULL_STRING)); + } + + @Test + void testGetDeclaredType() { + assertGetDeclaredType(NULL_TYPE); + assertGetDeclaredType(SELF_TYPE); + assertGetDeclaredType(SUPER_CLASS); + assertGetDeclaredType(ALL_TYPES); + assertGetDeclaredType(ALL_SUPER_TYPES); + assertGetDeclaredType(ALL_SUPER_CLASSES); + assertGetDeclaredType(ALL_SUPER_INTERFACES); + assertGetDeclaredType(SUPER_INTERFACES); + assertGetDeclaredType(SUPER_TYPES); + + assertGetDeclaredType(SELF_TYPE_PLUS_ALL_SUPER_TYPES); + assertGetDeclaredType(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + assertGetDeclaredType(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + assertGetDeclaredType(SELF_TYPE_PLUS_SUPER_CLASS); + assertGetDeclaredType(SELF_TYPE_PLUS_SUPER_INTERFACES); + assertGetDeclaredType(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testGetDeclaredTypeOnNull() { + assertNull(TypeUtils.getDeclaredType(this.processingEnv, NULL_TYPE)); + assertNull(TypeUtils.getDeclaredType(this.processingEnv, NULL_TYPE_MIRROR)); + assertNull(TypeUtils.getDeclaredType(this.processingEnv, NULL_STRING)); + + assertNull(getDeclaredType(NULL_TYPE)); + + assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE); + assertGetDeclaredTypeOnNullProcessingEnvironment(SUPER_CLASS); + assertGetDeclaredTypeOnNullProcessingEnvironment(ALL_TYPES); + assertGetDeclaredTypeOnNullProcessingEnvironment(ALL_SUPER_TYPES); + assertGetDeclaredTypeOnNullProcessingEnvironment(ALL_SUPER_CLASSES); + assertGetDeclaredTypeOnNullProcessingEnvironment(ALL_SUPER_INTERFACES); + assertGetDeclaredTypeOnNullProcessingEnvironment(SUPER_INTERFACES); + assertGetDeclaredTypeOnNullProcessingEnvironment(SUPER_TYPES); + + assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_TYPES); + assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS); + assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_INTERFACES); + assertGetDeclaredTypeOnNullProcessingEnvironment(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + @Test + void testToStringOnClasses() { + assertToStringOnClasses(); + } + + @Test + void testToStringOnArrayTypes() { + assertToStringOnArrayTypes(); + } + + @Test + void testToStringOnCollectionTypes() { + assertToStringOnCollectionTypes(); + } + + @Test + void testToStringMapTypes() { + assertToStringOnMapTypes(); + } + + @Test + void testToStringOnNull() { + assertNull(TypeUtils.toString(NULL_TYPE_MIRROR)); + } + + @Test + void testTypeElementFinderOnNull() { + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, true, true, true)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, true, true, false)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, true, false, true)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, true, false, false)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, true, false, true, true)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, true, false)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, false, true)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, false, false)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, true, true, true)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, true, true, false)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, true, false, true)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, true, false, false)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, true, true)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, true, false)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, false, true)); + + assertThrows(IllegalArgumentException.class, () -> typeElementFinder(NULL_TYPE_ELEMENT, false, false, false, false)); + } + + private void assertIsSameType(Element typeElement, Type type) { + assertTrue(isSameType(typeElement, type)); + assertTrue(isSameType(typeElement, type.getTypeName())); + assertTrue(isSameType(typeElement.asType(), type)); + assertTrue(isSameType(typeElement.asType(), type.getTypeName())); + } + + private void assertOfTypeMirrors(Class... types) { + Element[] elements = getElements(types); + assertEquals(getTypeMirrors(types), ofTypeMirrors(elements)); + } + + private void assertOfTypeElements(Class... types) { + List typesList = getTypeMirrors(types); + List typeElements = ofTypeElements(typesList); + for (TypeMirror typeMirror : typesList) { + assertTrue(typeElements.contains(ofTypeElement(typeMirror))); + } + } + + private void assertOfDeclaredTypes(Class... types) { + Element[] elements = getElements(types); + List declaredTypes = ofDeclaredTypes(elements); + assertDeclaredTypes(declaredTypes, types); + } + + private void assertIsArrayType(Type type) { + assertTrue(isArrayType(getFieldType(type, "integers"))); + assertTrue(isArrayType(getFieldType(type, "strings"))); + assertTrue(isArrayType(getFieldType(type, "primitiveTypeModels"))); + assertTrue(isArrayType(getFieldType(type, "models"))); + assertTrue(isArrayType(getFieldType(type, "colors"))); + } + + private void assertIsArrayType(Element element) { + assertTrue(isArrayType(findField(element, "integers"))); + assertTrue(isArrayType(findField(element, "strings"))); + assertTrue(isArrayType(findField(element, "primitiveTypeModels"))); + assertTrue(isArrayType(findField(element, "models"))); + assertTrue(isArrayType(findField(element, "colors"))); + } + + private void assertTypeMirrors(List typeMirrors, Type... types) { + int length = length(types); + for (int i = 0; i < length; i++) { + if (types[i] == null) { + length--; + } + } + assertEquals(length, typeMirrors.size()); + for (int i = 0; i < length; i++) { + assertSame(typeMirrors.get(i), getTypeMirror(types[i])); + } + } + + private void assertTypeElements(List typeElements, Type... types) { + int length = length(types); + for (int i = 0; i < length; i++) { + if (types[i] == null) { + length--; + } + } + assertEquals(length, typeElements.size()); + for (int i = 0; i < length; i++) { + assertSame(typeElements.get(i), getTypeElement(types[i])); + } + } + + private void assertDeclaredTypes(List declaredTypes, Type... types) { + int length = length(types); + for (int i = 0; i < length; i++) { + if (types[i] == null) { + length--; + } + } + assertEquals(length, declaredTypes.size()); + for (int i = 0; i < length; i++) { + assertSame(declaredTypes.get(i), getDeclaredType(types[i])); + } + } + + private void assertInterfaceTypeMirror(Element type, Type interfaceType) { + TypeMirror typeMirror = findInterfaceTypeMirror(type, interfaceType); + assertTrue(isSameType(typeMirror, interfaceType)); + + typeMirror = findInterfaceTypeMirror(type.asType(), interfaceType); + assertTrue(isSameType(typeMirror, interfaceType)); + } + + private void assertGetTypeMirrors(Type... types) { + List typeMirrors = TypeUtils.getTypeMirrors(processingEnv, types); + assertEquals(typeMirrors, getTypeMirrors(types)); + assertTypeMirrors(typeMirrors, types); + } + + private void assertGetTypeMirror(Type... types) { + int length = length(types); + for (int i = 0; i < length; i++) { + Type type = types[i]; + TypeMirror typeMirror = TypeUtils.getTypeMirror(processingEnv, type); + assertSame(getTypeMirror(type), typeMirror); + assertTrue(isSameType(typeMirror, type)); + } + } + + private void assertGetTypeMirrorOnNullProcessingEnvironment(Type... types) { + int length = length(types); + for (int i = 0; i < length; i++) { + Type type = types[i]; + TypeMirror typeMirror = TypeUtils.getTypeMirror(NULL_PROCESSING_ENVIRONMENT, type); + assertNull(typeMirror); + } + } + + private void assertGetTypeElementsWithProcessingEnvironment(Type... types) { + List typeElements = TypeUtils.getTypeElements(this.processingEnv, types); + assertEquals(getTypeElements(types), typeElements); + assertTypeElements(typeElements, types); + } + + private void assertGetTypeElementsOnNullProcessingEnvironment(Type... types) { + List typeElements = TypeUtils.getTypeElements(NULL_PROCESSING_ENVIRONMENT, types); + assertEmptyList(typeElements); + } + + private void assertGetDeclaredType(Type... types) { + int length = length(types); + for (int i = 0; i < length; i++) { + Type type = types[i]; + DeclaredType declaredType = TypeUtils.getDeclaredType(processingEnv, type); + assertSame(getDeclaredType(type), declaredType); + assertSame(getDeclaredType(type), TypeUtils.getDeclaredType(processingEnv, declaredType)); + assertTrue(isSameType(declaredType, type)); + } + } + + private void assertGetDeclaredTypeOnNullProcessingEnvironment(Type... types) { + int length = length(types); + for (int i = 0; i < length; i++) { + Type type = types[i]; + TypeMirror typeMirror = TypeUtils.getDeclaredType(NULL_PROCESSING_ENVIRONMENT, type); + assertNull(typeMirror); + } + } + + private void assertToStringOnMapTypes() { + assertToString(getFieldType(MapTypeModel.class, "strings")); + assertToString(getFieldType(MapTypeModel.class, "colors")); + assertToString(getFieldType(MapTypeModel.class, "primitiveTypeModels")); + assertToString(getFieldType(MapTypeModel.class, "models")); + assertToString(getFieldType(MapTypeModel.class, "modelArrays")); + } + + private void assertToStringOnCollectionTypes() { + assertToString(getFieldType(CollectionTypeModel.class, "strings")); + assertToString(getFieldType(CollectionTypeModel.class, "colors")); + assertToString(getFieldType(CollectionTypeModel.class, "primitiveTypeModels")); + assertToString(getFieldType(CollectionTypeModel.class, "models")); + assertToString(getFieldType(CollectionTypeModel.class, "modelArrays")); + } + + private void assertToStringOnArrayTypes() { + assertToString(getFieldType(ArrayTypeModel.class, "integers")); + assertToString(getFieldType(ArrayTypeModel.class, "strings")); + assertToString(getFieldType(ArrayTypeModel.class, "primitiveTypeModels")); + assertToString(getFieldType(ArrayTypeModel.class, "models")); + assertToString(getFieldType(ArrayTypeModel.class, "colors")); + } + + private TypeMirror getFieldType(Type type, String fieldName) { + TypeMirror typeMirror = getTypeMirror(type); + return findField(typeMirror, fieldName).asType(); + } + + private TypeMirror getFieldType(Element element, String fieldName) { + return findField(element, fieldName).asType(); + } + + private void assertToStringOnClasses() { + assertToString(NULL_TYPE); + assertToString(SELF_TYPE); + assertToString(SUPER_CLASS); + assertToString(ALL_TYPES); + assertToString(ALL_SUPER_TYPES); + assertToString(ALL_SUPER_CLASSES); + assertToString(ALL_SUPER_INTERFACES); + assertToString(SUPER_INTERFACES); + assertToString(SUPER_TYPES); + + assertToString(SELF_TYPE_PLUS_ALL_SUPER_TYPES); + assertToString(SELF_TYPE_PLUS_ALL_SUPER_CLASSES); + assertToString(SELF_TYPE_PLUS_ALL_SUPER_INTERFACES); + assertToString(SELF_TYPE_PLUS_SUPER_CLASS); + assertToString(SELF_TYPE_PLUS_SUPER_INTERFACES); + assertToString(SELF_TYPE_PLUS_SUPER_CLASS_PLUS_SUPER_INTERFACES); + } + + private void assertToString(Type... types) { + int length = length(types); + for (int i = 0; i < length; i++) { + if (types[i] == null) { + length--; + } + } + for (int i = 0; i < length; i++) { + TypeMirror typeMirror = getTypeMirror(types[i]); + assertEquals(types[i].getTypeName(), TypeUtils.toString(typeMirror)); + } + } + + private void assertToString(TypeMirror type) { + assertEquals(type.toString(), TypeUtils.toString(type)); + } +} \ No newline at end of file diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java new file mode 100644 index 000000000..9cad309cf --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java @@ -0,0 +1,88 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + +import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import java.lang.reflect.Type; +import java.util.List; + +import static io.microsphere.lang.model.util.ConstructorUtils.findConstructor; +import static io.microsphere.lang.model.util.FieldUtils.findField; +import static io.microsphere.lang.model.util.MethodUtils.findMethod; +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * The utilies class for testing + * + * @author Mercy + * @see AbstractAnnotationProcessingTest + * @since 1.0.0 + */ +public abstract class UtilTest extends AbstractAnnotationProcessingTest { + + protected List getTypeMirrors(Type... types) { + return TypeUtils.getTypeMirrors(processingEnv, types); + } + + protected TypeMirror getTypeMirror(Type type) { + return TypeUtils.getTypeMirror(processingEnv, type); + } + + protected List getTypeElements(Type... types) { + return TypeUtils.getTypeElements(processingEnv, types); + } + + protected TypeElement getTypeElement(Type type) { + return TypeUtils.getTypeElement(processingEnv, type); + } + + protected VariableElement getField(Type type, String fieldName) { + TypeElement typeElement = getTypeElement(type); + return findField(typeElement, fieldName); + } + + protected ExecutableElement getMethod(Type type, String methodName, Type... parameterTypes) { + TypeElement typeElement = getTypeElement(type); + return findMethod(typeElement, methodName, parameterTypes); + } + + protected ExecutableElement getConstructor(Type type, Type... parameterTypes) { + TypeElement typeElement = getTypeElement(type); + return findConstructor(typeElement, parameterTypes); + } + + protected Element[] getElements(Type... types) { + return getTypeMirrors(types).stream().map(TypeUtils::ofTypeElement).toArray(Element[]::new); + } + + protected DeclaredType getDeclaredType(Type type) { + return TypeUtils.getDeclaredType(processingEnv, type); + } + + protected void assertEmptyList(List list) { + assertSame(emptyList(), list); + } +} From 94feebdde850e30ba42a4cde9f176fea1b8eb1ff Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:04:40 +0800 Subject: [PATCH 022/438] Use lang-model utilities; update deps Replace references to io.microsphere.annotation.processor.util.* with the new io.microsphere.lang.model.util.* equivalents across the annotation processor (MessagerUtils, AnnotationUtils, ElementUtils, TypeUtils, ClassUtils, JSONElementVisitor). Update Javadoc and fully-qualified references where needed. Simplify ConfigurationPropertyAnnotationProcessorTest by using AbstractAnnotationProcessingTest and removing an unused lifecycle override. Update pom dependency management and dependencies to add microsphere-java-core and microsphere-lang-model, replace microsphere-java-core dependency usage with microsphere-jdk-tools, and add microsphere-java-test to reflect module relocations. --- ...figurationPropertyAnnotationProcessor.java | 2 +- .../annotation/processor/FilerProcessor.java | 2 +- .../processor/ResourceProcessor.java | 4 ++-- .../AnnotatedElementJSONElementVisitor.java | 11 +++++----- ...nfigurationPropertyJSONElementVisitor.java | 16 +++++++-------- ...rationPropertyAnnotationProcessorTest.java | 9 ++------- microsphere-java-dependencies/pom.xml | 20 ++++++++++++++++++- 7 files changed, 39 insertions(+), 25 deletions(-) diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessor.java index 547c702a2..6060855d6 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessor.java @@ -36,11 +36,11 @@ import java.util.Set; import static io.microsphere.annotation.processor.model.util.ConfigurationPropertyJSONElementVisitor.CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME; -import static io.microsphere.annotation.processor.util.MessagerUtils.printNote; import static io.microsphere.constants.ResourceConstants.CONFIGURATION_PROPERTY_METADATA_RESOURCE; import static io.microsphere.constants.SymbolConstants.COMMA_CHAR; import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET_CHAR; import static io.microsphere.constants.SymbolConstants.RIGHT_SQUARE_BRACKET_CHAR; +import static io.microsphere.lang.model.util.MessagerUtils.printNote; import static io.microsphere.metadata.ConfigurationPropertyLoader.loadAll; import static javax.lang.model.SourceVersion.latestSupported; import static javax.tools.StandardLocation.CLASS_OUTPUT; diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/FilerProcessor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/FilerProcessor.java index 36d1416b4..5a34c0fa5 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/FilerProcessor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/FilerProcessor.java @@ -24,7 +24,7 @@ import javax.tools.JavaFileManager; import java.util.function.BiFunction; -import static io.microsphere.annotation.processor.util.MessagerUtils.printMandatoryWarning; +import static io.microsphere.lang.model.util.MessagerUtils.printMandatoryWarning; import static io.microsphere.reflect.FieldUtils.getFieldValue; /** diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ResourceProcessor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ResourceProcessor.java index ab7b0c883..71852e810 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ResourceProcessor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ResourceProcessor.java @@ -34,8 +34,8 @@ import java.util.function.BiFunction; import java.util.function.Function; -import static io.microsphere.annotation.processor.util.MessagerUtils.printNote; -import static io.microsphere.annotation.processor.util.MessagerUtils.printWarning; +import static io.microsphere.lang.model.util.MessagerUtils.printNote; +import static io.microsphere.lang.model.util.MessagerUtils.printWarning; import static io.microsphere.util.ExceptionUtils.wrap; import static java.util.Optional.empty; import static java.util.Optional.of; diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/AnnotatedElementJSONElementVisitor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/AnnotatedElementJSONElementVisitor.java index b5c76b0b1..cbb1d6418 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/AnnotatedElementJSONElementVisitor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/AnnotatedElementJSONElementVisitor.java @@ -18,6 +18,7 @@ package io.microsphere.annotation.processor.model.util; import io.microsphere.annotation.Nonnull; +import io.microsphere.lang.model.util.JSONElementVisitor; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; @@ -29,16 +30,16 @@ import java.lang.annotation.ElementType; import java.lang.reflect.AnnotatedElement; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementTypes; -import static io.microsphere.annotation.processor.util.ElementUtils.matchesElementType; -import static io.microsphere.annotation.processor.util.TypeUtils.getDeclaredType; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementTypes; +import static io.microsphere.lang.model.util.ElementUtils.matchesElementType; +import static io.microsphere.lang.model.util.TypeUtils.getDeclaredType; import static io.microsphere.util.Assert.assertNotNull; /** * An abstract implementation of {@link ElementVisitor} that generates JSON content for elements * annotated with a specific annotation. * - *

This class extends {@link JSONElementVisitor}, providing functionality to filter and process + *

This class extends {@link io.microsphere.lang.model.util.JSONElementVisitor}, providing functionality to filter and process * only those elements that are annotated with the specified annotation. It leverages the annotation * processing environment to gather information about the annotated elements and constructs JSON * representations accordingly.

@@ -79,7 +80,7 @@ * type elements annotated with a custom annotation and generates JSON output for them.

* * @author Mercy - * @see JSONElementVisitor + * @see io.microsphere.lang.model.util.JSONElementVisitor * @see AnnotatedElement * @see Annotation * @see ElementType diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java index 963392408..278575b9f 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java @@ -31,22 +31,22 @@ import java.util.List; import java.util.Map; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAnnotation; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getAttributeName; -import static io.microsphere.annotation.processor.util.AnnotationUtils.getElementValues; -import static io.microsphere.annotation.processor.util.AnnotationUtils.matchesDefaultAttributeValue; -import static io.microsphere.annotation.processor.util.ClassUtils.getClassName; -import static io.microsphere.annotation.processor.util.TypeUtils.getTypeName; +import static io.microsphere.lang.model.util.AnnotationUtils.getAnnotation; +import static io.microsphere.lang.model.util.AnnotationUtils.getAttributeName; +import static io.microsphere.lang.model.util.AnnotationUtils.getElementValues; +import static io.microsphere.lang.model.util.AnnotationUtils.matchesDefaultAttributeValue; +import static io.microsphere.lang.model.util.ClassUtils.getClassName; +import static io.microsphere.lang.model.util.TypeUtils.getTypeName; import static io.microsphere.constants.SymbolConstants.COMMA_CHAR; import static io.microsphere.util.ServiceLoaderUtils.loadFirstService; import static io.microsphere.util.StringUtils.isBlank; /** - * {@link ConfigurationProperty @ConfigurationProperty}'s {@link AnnotatedElementJSONElementVisitor} based on + * {@link ConfigurationProperty @ConfigurationProperty}'s {@link io.microsphere.lang.model.util.AnnotatedElementJSONElementVisitor} based on * {@link ConfigurationPropertyGenerator} generating the JSON representation of the configuration property metadata. * * @author Mercy - * @see AnnotatedElementJSONElementVisitor + * @see io.microsphere.lang.model.util.AnnotatedElementJSONElementVisitor * @see ConfigurationProperty * @see io.microsphere.beans.ConfigurationProperty * @since 1.0.0 diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessorTest.java index 82693aa0f..2fd190bc9 100644 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessorTest.java +++ b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessorTest.java @@ -23,6 +23,7 @@ import io.microsphere.io.StandardFileWatchService; import io.microsphere.reflect.MethodUtils; import io.microsphere.reflect.TypeUtils; +import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; import io.microsphere.util.ServiceLoaderUtils; import org.junit.jupiter.api.Test; @@ -39,12 +40,6 @@ * @since 1.0.0 */ class ConfigurationPropertyAnnotationProcessorTest extends AbstractAnnotationProcessingTest { - - @Override - protected void beforeTest() { - super.beforeTest(); - } - @Override protected void addCompiledClasses(Set> compiledClasses) { compiledClasses.add(ManifestArtifactResourceResolver.class); @@ -60,4 +55,4 @@ protected void addCompiledClasses(Set> compiledClasses) { void testConstants() { assertEquals("io.microsphere.annotation.ConfigurationProperty", CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME); } -} +} \ No newline at end of file diff --git a/microsphere-java-dependencies/pom.xml b/microsphere-java-dependencies/pom.xml index 1f36e1c37..c1fd80661 100644 --- a/microsphere-java-dependencies/pom.xml +++ b/microsphere-java-dependencies/pom.xml @@ -21,6 +21,18 @@ + + io.github.microsphere-projects + microsphere-java-core + ${revision} + + + + io.github.microsphere-projects + microsphere-lang-model + ${revision} + + io.github.microsphere-projects microsphere-annotation-processor @@ -29,7 +41,13 @@ io.github.microsphere-projects - microsphere-java-core + microsphere-jdk-tools + ${revision} + + + + io.github.microsphere-projects + microsphere-java-test ${revision} From 9cfeefbd771d525227f89a04f9b6f183bdaa68eb Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:04:46 +0800 Subject: [PATCH 023/438] Update modules list in parent POM Add new modules to the parent POM and reorder existing entries. This commit introduces microsphere-jdk-tools, microsphere-java-test, and microsphere-lang-model to the section and moves microsphere-annotation-processor to the end of the list so the module ordering reflects the updated project structure. --- pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3f1676d72..1b85ffa97 100644 --- a/pom.xml +++ b/pom.xml @@ -57,8 +57,11 @@ microsphere-java-parent microsphere-java-dependencies - microsphere-annotation-processor microsphere-java-core + microsphere-jdk-tools + microsphere-java-test + microsphere-lang-model + microsphere-annotation-processor \ No newline at end of file From 89ac266b313dc7b0020fb3a983f8bc4b6c99fe97 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:08:48 +0800 Subject: [PATCH 024/438] Remove compiler and shade plugins from module POMs Remove the sections from microsphere-java-test, microsphere-jdk-tools, and microsphere-lang-model POMs. This deletes the maven-compiler-plugin configuration (compilerArgument -proc:none) and the maven-shade-plugin execution that shaded io.github.microsphere-projects:microsphere-java-core, consolidating/cleaning up module-level build configuration. --- microsphere-java-test/pom.xml | 31 ------------------------------- microsphere-jdk-tools/pom.xml | 32 -------------------------------- microsphere-lang-model/pom.xml | 31 ------------------------------- 3 files changed, 94 deletions(-) diff --git a/microsphere-java-test/pom.xml b/microsphere-java-test/pom.xml index 9d94d023b..e59a339d2 100644 --- a/microsphere-java-test/pom.xml +++ b/microsphere-java-test/pom.xml @@ -99,35 +99,4 @@ - - - - org.apache.maven.plugins - maven-compiler-plugin - - -proc:none - - - - org.apache.maven.plugins - maven-shade-plugin - - - package - - shade - - - - - io.github.microsphere-projects:microsphere-java-core - - - - - - - - - \ No newline at end of file diff --git a/microsphere-jdk-tools/pom.xml b/microsphere-jdk-tools/pom.xml index d16b92c62..b0822a8de 100644 --- a/microsphere-jdk-tools/pom.xml +++ b/microsphere-jdk-tools/pom.xml @@ -55,36 +55,4 @@
- - - - - org.apache.maven.plugins - maven-compiler-plugin - - -proc:none - - - - org.apache.maven.plugins - maven-shade-plugin - - - package - - shade - - - - - io.github.microsphere-projects:microsphere-java-core - - - - - - - - - \ No newline at end of file diff --git a/microsphere-lang-model/pom.xml b/microsphere-lang-model/pom.xml index 2c674b268..83947daa6 100644 --- a/microsphere-lang-model/pom.xml +++ b/microsphere-lang-model/pom.xml @@ -99,35 +99,4 @@ - - - - org.apache.maven.plugins - maven-compiler-plugin - - -proc:none - - - - org.apache.maven.plugins - maven-shade-plugin - - - package - - shade - - - - - io.github.microsphere-projects:microsphere-java-core - - - - - - - - - \ No newline at end of file From 7956a2041190200c5643416cd4cbe99b2b263bd2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:17:50 +0800 Subject: [PATCH 025/438] Centralize JAX API versions; remove test processor Move javax.ws.rs and jaxws-api version properties into the parent pom and add dependencyManagement entries for javax.ws.rs-api and jaxws-api. Remove duplicate properties and explicit version elements from module poms (annotation-processor, lang-model, java-test) so they inherit the parent-managed versions. Adjust microsphere-java-dependencies to reorder artifactIds (swap mappings between microsphere-lang-model, microsphere-jdk-tools, microsphere-annotation-processor, and microsphere-java-test). Delete the TestProcessor test class and its service registration (META-INF/services) from the java-test module. --- microsphere-annotation-processor/pom.xml | 7 --- microsphere-java-dependencies/pom.xml | 8 ++-- microsphere-java-parent/pom.xml | 19 +++++++- microsphere-java-test/pom.xml | 7 --- .../annotation/processing/TestProcessor.java | 48 ------------------- .../javax.annotation.processing.Processor | 1 - microsphere-lang-model/pom.xml | 7 --- 7 files changed, 22 insertions(+), 75 deletions(-) delete mode 100644 microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/TestProcessor.java delete mode 100644 microsphere-java-test/src/test/resources/META-INF/services/javax.annotation.processing.Processor diff --git a/microsphere-annotation-processor/pom.xml b/microsphere-annotation-processor/pom.xml index e5558b6fe..26473f3d3 100644 --- a/microsphere-annotation-processor/pom.xml +++ b/microsphere-annotation-processor/pom.xml @@ -18,11 +18,6 @@ Microsphere :: Java :: Annotation Processor Microsphere Annotation Processor - - 2.1 - 2.3.1 - - @@ -79,7 +74,6 @@ javax.ws.rs javax.ws.rs-api - ${javax.ws.rs.version} test @@ -87,7 +81,6 @@ javax.xml.ws jaxws-api - ${jaxws-api.version} test diff --git a/microsphere-java-dependencies/pom.xml b/microsphere-java-dependencies/pom.xml index c1fd80661..2be116cb7 100644 --- a/microsphere-java-dependencies/pom.xml +++ b/microsphere-java-dependencies/pom.xml @@ -29,25 +29,25 @@ io.github.microsphere-projects - microsphere-lang-model + microsphere-jdk-tools ${revision} io.github.microsphere-projects - microsphere-annotation-processor + microsphere-java-test ${revision} io.github.microsphere-projects - microsphere-jdk-tools + microsphere-lang-model ${revision} io.github.microsphere-projects - microsphere-java-test + microsphere-annotation-processor ${revision} diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 4d1d66ae9..ee94b7b74 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -20,6 +20,8 @@ 1.3.2 + 2.1 + 2.3.1 3.0.2 1.3.2 7.0.2 @@ -31,7 +33,6 @@ 1.37 - @@ -43,6 +44,22 @@ ${javax.annotation-api.version} + + + javax.ws.rs + javax.ws.rs-api + ${javax.ws.rs.version} + true + + + + + javax.xml.ws + jaxws-api + ${jaxws-api.version} + true + + com.google.code.findbugs diff --git a/microsphere-java-test/pom.xml b/microsphere-java-test/pom.xml index e59a339d2..568f1add5 100644 --- a/microsphere-java-test/pom.xml +++ b/microsphere-java-test/pom.xml @@ -18,11 +18,6 @@ Microsphere :: Java :: Test Microsphere Java Test - - 2.1 - 2.3.1 - - @@ -72,7 +67,6 @@ javax.ws.rs javax.ws.rs-api - ${javax.ws.rs.version} true @@ -80,7 +74,6 @@ javax.xml.ws jaxws-api - ${jaxws-api.version} true diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/TestProcessor.java b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/TestProcessor.java deleted file mode 100644 index e1ad024a5..000000000 --- a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/TestProcessor.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.test.annotation.processing; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.Processor; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.TypeElement; -import java.util.Set; - -import static javax.lang.model.SourceVersion.latestSupported; - -/** - * Test {@link Processor} - * - * @author Mercy - * @see Processor - * @since 1.0.0 - */ -@SupportedAnnotationTypes("*") -public class TestProcessor extends AbstractProcessor { - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - return true; - } - - @Override - public SourceVersion getSupportedSourceVersion() { - return latestSupported(); - } -} \ No newline at end of file diff --git a/microsphere-java-test/src/test/resources/META-INF/services/javax.annotation.processing.Processor b/microsphere-java-test/src/test/resources/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index 6b85e96b8..000000000 --- a/microsphere-java-test/src/test/resources/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1 +0,0 @@ -io.microsphere.test.annotation.processing.TestProcessor \ No newline at end of file diff --git a/microsphere-lang-model/pom.xml b/microsphere-lang-model/pom.xml index 83947daa6..101084931 100644 --- a/microsphere-lang-model/pom.xml +++ b/microsphere-lang-model/pom.xml @@ -18,11 +18,6 @@ Microsphere :: Java :: Language Model Microsphere Language Model - - 2.1 - 2.3.1 - - @@ -72,7 +67,6 @@ javax.ws.rs javax.ws.rs-api - ${javax.ws.rs.version} test @@ -80,7 +74,6 @@ javax.xml.ws jaxws-api - ${jaxws-api.version} test From a7185d67075f2eaf8f43028627833dc83874777b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:18:10 +0800 Subject: [PATCH 026/438] Clean up imports in FieldUtils and tests Reorder and group static imports in FieldUtils for consistency and readability. Remove an unused import (AbstractAnnotationProcessingTest) from JSONElementVisitorTest to eliminate a compiler/IDE warning. No behavior changes. --- .../java/io/microsphere/lang/model/util/FieldUtils.java | 6 +++--- .../microsphere/lang/model/util/JSONElementVisitorTest.java | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/FieldUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/FieldUtils.java index 3ded2017e..b14dd47a1 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/FieldUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/FieldUtils.java @@ -29,14 +29,14 @@ import java.util.List; import java.util.function.Predicate; +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static io.microsphere.lang.function.Streams.filterFirst; import static io.microsphere.lang.model.util.ElementUtils.filterElements; import static io.microsphere.lang.model.util.ElementUtils.hasModifiers; import static io.microsphere.lang.model.util.ElementUtils.matchesElementKind; import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; import static io.microsphere.lang.model.util.TypeUtils.isEnumType; -import static io.microsphere.collection.CollectionUtils.isEmpty; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; -import static io.microsphere.lang.function.Streams.filterFirst; import static java.util.Collections.emptyList; import static javax.lang.model.element.ElementKind.ENUM_CONSTANT; import static javax.lang.model.element.ElementKind.FIELD; diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java index 4cc8234f9..c18e83147 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java @@ -19,7 +19,6 @@ import io.microsphere.test.annotation.TestAnnotation; -import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; import io.microsphere.test.model.Color; import io.microsphere.test.model.StringArrayList; import org.junit.jupiter.api.BeforeEach; From 91949bd290cad022ca0139ccb8229623f16ebbd7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:18:26 +0800 Subject: [PATCH 027/438] Reorder and clean up imports across modules Normalize and tidy imports across multiple modules: reorder static imports for consistency, remove unused imports (including AbstractAnnotationProcessingTest in tests), and fix minor formatting. Replace Mockito wildcard import with explicit mock import in TestServiceImplTest. These are code-cleanup changes to reduce warnings and improve import organization. --- .../ConfigurationPropertyJSONElementVisitor.java | 2 +- .../annotation/processor/FilerProcessorTest.java | 1 - .../test/service/TestServiceImplTest.java | 2 +- .../lang/model/util/AnnotationUtils.java | 14 +++++++------- .../io/microsphere/lang/model/util/ClassUtils.java | 2 +- .../lang/model/util/ConstructorUtils.java | 6 +++--- .../microsphere/lang/model/util/ElementUtils.java | 2 +- .../microsphere/lang/model/util/MemberUtils.java | 2 +- .../microsphere/lang/model/util/MethodUtils.java | 8 ++++---- .../util/ResolvableAnnotationValueVisitor.java | 2 +- .../lang/model/util/ConstructorUtilsTest.java | 1 - .../util/ExecutableElementComparatorTest.java | 1 - 12 files changed, 20 insertions(+), 23 deletions(-) diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java index 278575b9f..5f5b9f2f7 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java @@ -31,13 +31,13 @@ import java.util.List; import java.util.Map; +import static io.microsphere.constants.SymbolConstants.COMMA_CHAR; import static io.microsphere.lang.model.util.AnnotationUtils.getAnnotation; import static io.microsphere.lang.model.util.AnnotationUtils.getAttributeName; import static io.microsphere.lang.model.util.AnnotationUtils.getElementValues; import static io.microsphere.lang.model.util.AnnotationUtils.matchesDefaultAttributeValue; import static io.microsphere.lang.model.util.ClassUtils.getClassName; import static io.microsphere.lang.model.util.TypeUtils.getTypeName; -import static io.microsphere.constants.SymbolConstants.COMMA_CHAR; import static io.microsphere.util.ServiceLoaderUtils.loadFirstService; import static io.microsphere.util.StringUtils.isBlank; diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/FilerProcessorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/FilerProcessorTest.java index 132685903..8f715df0f 100644 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/FilerProcessorTest.java +++ b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/FilerProcessorTest.java @@ -25,7 +25,6 @@ import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; - import java.lang.reflect.Method; import static io.microsphere.annotation.processor.ResourceProcessor.exists; diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/service/TestServiceImplTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/service/TestServiceImplTest.java index 272aba056..b7fc3ecf8 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/service/TestServiceImplTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/service/TestServiceImplTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; /** * Unit tests for the TestServiceImpl class. diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java index bbfce4988..53462b44f 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java @@ -42,13 +42,6 @@ import java.util.Objects; import java.util.function.Predicate; -import static io.microsphere.lang.model.util.MethodUtils.findDeclaredMethods; -import static io.microsphere.lang.model.util.MethodUtils.getDeclaredMethods; -import static io.microsphere.lang.model.util.MethodUtils.getMethodName; -import static io.microsphere.lang.model.util.TypeUtils.getAllTypeElements; -import static io.microsphere.lang.model.util.TypeUtils.getTypeElement; -import static io.microsphere.lang.model.util.TypeUtils.isSameType; -import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; import static io.microsphere.collection.CollectionUtils.isEmpty; import static io.microsphere.collection.CollectionUtils.size; import static io.microsphere.collection.MapUtils.immutableEntry; @@ -56,6 +49,13 @@ import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; import static io.microsphere.lang.function.Streams.filterAll; +import static io.microsphere.lang.model.util.MethodUtils.findDeclaredMethods; +import static io.microsphere.lang.model.util.MethodUtils.getDeclaredMethods; +import static io.microsphere.lang.model.util.MethodUtils.getMethodName; +import static io.microsphere.lang.model.util.TypeUtils.getAllTypeElements; +import static io.microsphere.lang.model.util.TypeUtils.getTypeElement; +import static io.microsphere.lang.model.util.TypeUtils.isSameType; +import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; import static io.microsphere.util.ArrayUtils.isNotEmpty; import static io.microsphere.util.StringUtils.isBlank; import static java.util.Collections.emptyList; diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ClassUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ClassUtils.java index 1c2239399..7b83b0c99 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ClassUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ClassUtils.java @@ -21,9 +21,9 @@ import javax.lang.model.type.TypeMirror; -import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; import static io.microsphere.constants.SymbolConstants.DOLLAR_CHAR; import static io.microsphere.constants.SymbolConstants.DOT_CHAR; +import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; import static io.microsphere.util.ClassLoaderUtils.resolveClass; diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ConstructorUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ConstructorUtils.java index eb878ed96..c864c1c7d 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ConstructorUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ConstructorUtils.java @@ -32,12 +32,12 @@ import java.util.List; import java.util.function.Predicate; -import static io.microsphere.lang.model.util.ElementUtils.filterElements; -import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypes; -import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; import static io.microsphere.collection.CollectionUtils.isEmpty; import static io.microsphere.collection.ListUtils.first; import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static io.microsphere.lang.model.util.ElementUtils.filterElements; +import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypes; +import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; import static java.util.Collections.emptyList; import static javax.lang.model.util.ElementFilter.constructorsIn; diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ElementUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ElementUtils.java index f3d708d8a..f24373a19 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ElementUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ElementUtils.java @@ -32,9 +32,9 @@ import java.util.Set; import java.util.function.Predicate; -import static io.microsphere.lang.model.util.TypeUtils.isSameType; import static io.microsphere.collection.CollectionUtils.isEmpty; import static io.microsphere.lang.function.Predicates.and; +import static io.microsphere.lang.model.util.TypeUtils.isSameType; import static io.microsphere.reflect.TypeUtils.getTypeNames; import static io.microsphere.util.ArrayUtils.isNotEmpty; import static io.microsphere.util.ArrayUtils.length; diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MemberUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MemberUtils.java index addc57158..77f027a4f 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MemberUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MemberUtils.java @@ -27,10 +27,10 @@ import java.util.List; import java.util.function.Predicate; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; import static io.microsphere.lang.model.util.ElementUtils.filterElements; import static io.microsphere.lang.model.util.TypeUtils.getAllDeclaredTypes; import static io.microsphere.lang.model.util.TypeUtils.ofTypeElement; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java index 925d3ee37..f4e741343 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java @@ -34,6 +34,10 @@ import java.util.Objects; import java.util.function.Predicate; +import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; +import static io.microsphere.lang.function.Predicates.and; +import static io.microsphere.lang.function.Streams.filterFirst; import static io.microsphere.lang.model.util.ElementUtils.filterElements; import static io.microsphere.lang.model.util.ElementUtils.isPublicNonStatic; import static io.microsphere.lang.model.util.ElementUtils.matchParameterTypeNames; @@ -41,10 +45,6 @@ import static io.microsphere.lang.model.util.MemberUtils.getDeclaredMembers; import static io.microsphere.lang.model.util.TypeUtils.isSameType; import static io.microsphere.lang.model.util.TypeUtils.ofDeclaredType; -import static io.microsphere.collection.CollectionUtils.isEmpty; -import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; -import static io.microsphere.lang.function.Predicates.and; -import static io.microsphere.lang.function.Streams.filterFirst; import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; import static io.microsphere.util.ArrayUtils.EMPTY_TYPE_ARRAY; import static io.microsphere.util.ArrayUtils.isNotEmpty; diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java index 7a4653fe8..86bce41b5 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitor.java @@ -30,10 +30,10 @@ import java.util.Map; import java.util.Map.Entry; +import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; import static io.microsphere.lang.model.util.AnnotationUtils.getAttributeName; import static io.microsphere.lang.model.util.AnnotationUtils.getElementValues; import static io.microsphere.lang.model.util.ClassUtils.loadClass; -import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; import static io.microsphere.reflect.MethodUtils.findMethod; import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; import static io.microsphere.util.ArrayUtils.newArray; diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ConstructorUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ConstructorUtilsTest.java index 8fd8e5eb5..a4b513650 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ConstructorUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ConstructorUtilsTest.java @@ -18,7 +18,6 @@ package io.microsphere.lang.model.util; -import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; import org.junit.jupiter.api.Test; import org.springframework.core.env.Environment; diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ExecutableElementComparatorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ExecutableElementComparatorTest.java index de416226f..9043220bb 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ExecutableElementComparatorTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ExecutableElementComparatorTest.java @@ -1,6 +1,5 @@ package io.microsphere.lang.model.util; -import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; import io.microsphere.test.service.TestService; import org.junit.jupiter.api.Test; From ccfd34451d86d20760edfb6fb75a160e5fe832ec Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:35:02 +0800 Subject: [PATCH 028/438] Move annotations to dedicated module Create a new microsphere-java-annotations module and move annotation sources and tests from microsphere-java-core into it. Added a module POM (including jsr305 and JUnit test deps), new NonnullTest and NullableTest classes, and updated test Javadocs. Updated microsphere-java-core POM to depend on the new annotations artifact (replacing the previous direct jsr305 dependency), added the annotations artifact to dependencyManagement, and registered the new module in the root POM. Also removed an unused import in Since.java. --- microsphere-java-annotations/pom.xml | 44 +++++++++++++++++++ .../annotation/ConfigurationProperty.java | 2 +- .../microsphere/annotation/Experimental.java | 2 +- .../io/microsphere/annotation/Immutable.java | 2 +- .../io/microsphere/annotation/Nonnull.java | 2 +- .../io/microsphere/annotation/Nullable.java | 2 +- .../java/io/microsphere/annotation/Since.java | 7 +-- .../annotation/ConfigurationPropertyTest.java | 2 +- .../annotation/ExperimentalTest.java | 2 +- .../microsphere/annotation/ImmutableTest.java | 2 +- .../microsphere/annotation/NonnullTest.java | 38 ++++++++++++++++ .../microsphere/annotation/NullableTest.java | 38 ++++++++++++++++ .../io/microsphere/annotation/SinceTest.java | 2 +- microsphere-java-core/pom.xml | 8 ++-- microsphere-java-dependencies/pom.xml | 6 +++ pom.xml | 1 + 16 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 microsphere-java-annotations/pom.xml rename {microsphere-java-core => microsphere-java-annotations}/src/main/java/io/microsphere/annotation/ConfigurationProperty.java (99%) rename {microsphere-java-core => microsphere-java-annotations}/src/main/java/io/microsphere/annotation/Experimental.java (99%) rename {microsphere-java-core => microsphere-java-annotations}/src/main/java/io/microsphere/annotation/Immutable.java (99%) rename {microsphere-java-core => microsphere-java-annotations}/src/main/java/io/microsphere/annotation/Nonnull.java (99%) rename {microsphere-java-core => microsphere-java-annotations}/src/main/java/io/microsphere/annotation/Nullable.java (99%) rename {microsphere-java-core => microsphere-java-annotations}/src/main/java/io/microsphere/annotation/Since.java (96%) rename {microsphere-java-core => microsphere-java-annotations}/src/test/java/io/microsphere/annotation/ConfigurationPropertyTest.java (96%) rename {microsphere-java-core => microsphere-java-annotations}/src/test/java/io/microsphere/annotation/ExperimentalTest.java (96%) rename {microsphere-java-core => microsphere-java-annotations}/src/test/java/io/microsphere/annotation/ImmutableTest.java (96%) create mode 100644 microsphere-java-annotations/src/test/java/io/microsphere/annotation/NonnullTest.java create mode 100644 microsphere-java-annotations/src/test/java/io/microsphere/annotation/NullableTest.java rename {microsphere-java-core => microsphere-java-annotations}/src/test/java/io/microsphere/annotation/SinceTest.java (99%) diff --git a/microsphere-java-annotations/pom.xml b/microsphere-java-annotations/pom.xml new file mode 100644 index 000000000..e80e54ea5 --- /dev/null +++ b/microsphere-java-annotations/pom.xml @@ -0,0 +1,44 @@ + + + + io.github.microsphere-projects + microsphere-java-parent + ${revision} + ../microsphere-java-parent/pom.xml + + 4.0.0 + + io.github.microsphere-projects + microsphere-java-annotations + ${revision} + jar + + Microsphere :: Java :: Annotations + Microsphere Java Annotations + + + + + + com.google.code.findbugs + jsr305 + true + + + + + org.junit.jupiter + junit-jupiter + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/annotation/ConfigurationProperty.java b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/ConfigurationProperty.java similarity index 99% rename from microsphere-java-core/src/main/java/io/microsphere/annotation/ConfigurationProperty.java rename to microsphere-java-annotations/src/main/java/io/microsphere/annotation/ConfigurationProperty.java index dd6272f95..10203087e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/annotation/ConfigurationProperty.java +++ b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/ConfigurationProperty.java @@ -94,4 +94,4 @@ */ String[] source() default {}; -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/annotation/Experimental.java b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Experimental.java similarity index 99% rename from microsphere-java-core/src/main/java/io/microsphere/annotation/Experimental.java rename to microsphere-java-annotations/src/main/java/io/microsphere/annotation/Experimental.java index 498df0028..e3780f909 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/annotation/Experimental.java +++ b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Experimental.java @@ -49,4 +49,4 @@ */ String description() default ""; -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/annotation/Immutable.java b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Immutable.java similarity index 99% rename from microsphere-java-core/src/main/java/io/microsphere/annotation/Immutable.java rename to microsphere-java-annotations/src/main/java/io/microsphere/annotation/Immutable.java index 593118b5e..0ae81044f 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/annotation/Immutable.java +++ b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Immutable.java @@ -31,4 +31,4 @@ @Documented @Retention(RUNTIME) public @interface Immutable { -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/annotation/Nonnull.java b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Nonnull.java similarity index 99% rename from microsphere-java-core/src/main/java/io/microsphere/annotation/Nonnull.java rename to microsphere-java-annotations/src/main/java/io/microsphere/annotation/Nonnull.java index 859ec90a4..0a36bb87a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/annotation/Nonnull.java +++ b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Nonnull.java @@ -35,4 +35,4 @@ @javax.annotation.Nonnull @TypeQualifierNickname public @interface Nonnull { -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/annotation/Nullable.java b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Nullable.java similarity index 99% rename from microsphere-java-core/src/main/java/io/microsphere/annotation/Nullable.java rename to microsphere-java-annotations/src/main/java/io/microsphere/annotation/Nullable.java index c3a3a49e0..9b708f889 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/annotation/Nullable.java +++ b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Nullable.java @@ -36,4 +36,4 @@ @javax.annotation.Nonnull(when = MAYBE) @TypeQualifierNickname public @interface Nullable { -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/annotation/Since.java b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Since.java similarity index 96% rename from microsphere-java-core/src/main/java/io/microsphere/annotation/Since.java rename to microsphere-java-annotations/src/main/java/io/microsphere/annotation/Since.java index bb840ccbc..c2ec0c22d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/annotation/Since.java +++ b/microsphere-java-annotations/src/main/java/io/microsphere/annotation/Since.java @@ -16,8 +16,6 @@ */ package io.microsphere.annotation; -import io.microsphere.util.Version; - import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -38,7 +36,6 @@ * The annotation that indicates the API is introduced in the first time. * * @author Mercy - * @see Version * @see Experimental * @since 1.0.0 */ @@ -65,7 +62,7 @@ /** * @return The version value of the API, e.g. 1.0.0 - * @see Version */ String value(); -} + +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/annotation/ConfigurationPropertyTest.java b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/ConfigurationPropertyTest.java similarity index 96% rename from microsphere-java-core/src/test/java/io/microsphere/annotation/ConfigurationPropertyTest.java rename to microsphere-java-annotations/src/test/java/io/microsphere/annotation/ConfigurationPropertyTest.java index 54c7d50c6..c31732d3a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/annotation/ConfigurationPropertyTest.java +++ b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/ConfigurationPropertyTest.java @@ -25,7 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** - * {@link ConfigurationProperty} Test + * {@link ConfigurationProperty @ConfigurationProperty} Test * * @author Mercy * @see ConfigurationProperty diff --git a/microsphere-java-core/src/test/java/io/microsphere/annotation/ExperimentalTest.java b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/ExperimentalTest.java similarity index 96% rename from microsphere-java-core/src/test/java/io/microsphere/annotation/ExperimentalTest.java rename to microsphere-java-annotations/src/test/java/io/microsphere/annotation/ExperimentalTest.java index 9014ea8a9..d4db9a409 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/annotation/ExperimentalTest.java +++ b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/ExperimentalTest.java @@ -21,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; /** - * {@link Experimental} Test + * {@link Experimental @Experimental} Test * * @author Mercy * @see Experimental diff --git a/microsphere-java-core/src/test/java/io/microsphere/annotation/ImmutableTest.java b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/ImmutableTest.java similarity index 96% rename from microsphere-java-core/src/test/java/io/microsphere/annotation/ImmutableTest.java rename to microsphere-java-annotations/src/test/java/io/microsphere/annotation/ImmutableTest.java index 5d871cf1d..6ae1ffb79 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/annotation/ImmutableTest.java +++ b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/ImmutableTest.java @@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; /** - * {@link Immutable} Test + * {@link Immutable @Immutable} Test * * @author Mercy * @see Immutable diff --git a/microsphere-java-annotations/src/test/java/io/microsphere/annotation/NonnullTest.java b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/NonnullTest.java new file mode 100644 index 000000000..cea51bd42 --- /dev/null +++ b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/NonnullTest.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.microsphere.annotation; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * {@link Nonnull @Nonnull} Test + * + * @author Mercy + * @see Nonnull + * @since 1.0.0 + */ +@Nonnull +class NonnullTest { + + @Test + void test() { + assertNotNull(NonnullTest.class.getAnnotation(Nonnull.class)); + } +} \ No newline at end of file diff --git a/microsphere-java-annotations/src/test/java/io/microsphere/annotation/NullableTest.java b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/NullableTest.java new file mode 100644 index 000000000..25a6625fe --- /dev/null +++ b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/NullableTest.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.microsphere.annotation; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * {@link Nullable @Nullable} Test + * + * @author Mercy + * @see Nullable + * @since 1.0.0 + */ +@Nullable +class NullableTest { + + @Test + void test() { + assertNotNull(NullableTest.class.getAnnotation(Nullable.class)); + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/annotation/SinceTest.java b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/SinceTest.java similarity index 99% rename from microsphere-java-core/src/test/java/io/microsphere/annotation/SinceTest.java rename to microsphere-java-annotations/src/test/java/io/microsphere/annotation/SinceTest.java index 1ecca38c8..1e6314b95 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/annotation/SinceTest.java +++ b/microsphere-java-annotations/src/test/java/io/microsphere/annotation/SinceTest.java @@ -36,4 +36,4 @@ void test() { assertEquals("microsphere-java-core", since.module()); assertEquals("1.0.0", since.value()); } -} +} \ No newline at end of file diff --git a/microsphere-java-core/pom.xml b/microsphere-java-core/pom.xml index c6063daa5..ed6e35fdb 100644 --- a/microsphere-java-core/pom.xml +++ b/microsphere-java-core/pom.xml @@ -20,11 +20,11 @@ - + - com.google.code.findbugs - jsr305 - true + io.github.microsphere-projects + microsphere-java-annotations + ${revision} diff --git a/microsphere-java-dependencies/pom.xml b/microsphere-java-dependencies/pom.xml index 2be116cb7..aba268cdb 100644 --- a/microsphere-java-dependencies/pom.xml +++ b/microsphere-java-dependencies/pom.xml @@ -21,6 +21,12 @@ + + io.github.microsphere-projects + microsphere-java-annotations + ${revision} + + io.github.microsphere-projects microsphere-java-core diff --git a/pom.xml b/pom.xml index 1b85ffa97..33ed4932f 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,7 @@ microsphere-java-parent microsphere-java-dependencies + microsphere-java-annotations microsphere-java-core microsphere-jdk-tools microsphere-java-test From b7f1af5bec7b777929e0864aeeab02f40e003e68 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:36:05 +0800 Subject: [PATCH 029/438] Bump parent POM version to 0.2.3 Update the project's parent POM from io.github.microsphere-projects:microsphere-build:0.2.2 to 0.2.3 to incorporate upstream build configuration changes and fixes. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 33ed4932f..dc5c6a270 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.microsphere-projects microsphere-build - 0.2.2 + 0.2.3 io.github.microsphere-projects From f4300ecbf81cb6386fbb66f8b844c153581090b6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:40:14 +0800 Subject: [PATCH 030/438] Add jsr305 test dependency Add JSR-305 (com.google.code.findbugs:jsr305) as a test-scoped dependency in microsphere-java-core/pom.xml. This brings in standard nullability annotations for use in tests and static analysis during the test phase. --- microsphere-java-core/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/microsphere-java-core/pom.xml b/microsphere-java-core/pom.xml index ed6e35fdb..c31c60b8c 100644 --- a/microsphere-java-core/pom.xml +++ b/microsphere-java-core/pom.xml @@ -62,6 +62,13 @@ test + + + com.google.code.findbugs + jsr305 + test + + ch.qos.logback From 51162497bbc9917844ba7ecf3ce538e4e15f2f68 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:43:33 +0800 Subject: [PATCH 031/438] Update Maven Wrapper to 3.3.4 Replace checked-in maven-wrapper binary with the newer wrapper scripts and properties. Removes .mvn/wrapper/maven-wrapper.jar, updates .mvn/wrapper/maven-wrapper.properties to wrapperVersion=3.3.4 and points distributionUrl to an aliyun mirror (apache-maven-3.9.9) using distributionType=only-script. Replaces mvnw and mvnw.cmd with revamped wrapper implementations that download/install Maven on demand, add support for MVNW_REPOURL/MVNW_USERNAME/MVNW_PASSWORD/MVNW_VERBOSE, improve Java detection and error handling, support checksum validation, and add mvnd handling. --- .mvn/wrapper/maven-wrapper.jar | Bin 59925 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 19 +- mvnw | 483 ++++++++++++-------------- mvnw.cmd | 377 ++++++++++---------- 4 files changed, 423 insertions(+), 456 deletions(-) delete mode 100755 .mvn/wrapper/maven-wrapper.jar diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100755 index bf82ff01c6cdae4a1bb754a6e062954d77ac5c11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59925 zcmb5U1CS=sk~ZA7ZQHhc+Mc%Ywrx+_*0gQgw(Xv_ZBOg(y}RG;-uU;sUu;#Jh>EHw zGfrmZsXF;&D$0O@!2kh40RbILm8t;!w*&h7T24$wm|jX=oKf)`hV~7E`UmXw?e4Pt z`>_l#5YYGC|ANU0%S(xiDXTEZiATrw!Spl1gyQYxsqjrZO`%3Yq?k$Dr=tVr?HIeHlsmnE9=ZU6I2QoCjlLn85rrn7M!RO}+ z%|6^Q>sv`K3j6Ux>as6NoB}L8q#ghm_b)r{V+Pf3xj>b^+M8ZFY`k|FHgl zM!^0D!qDCjU~cj+fXM$0v@vuwvHcft?EeYw=4fbdZ{qkb#PI)>7{J=%Ux*@pi~i^9 z{(nu6>i-Y^_7lUudx7B}(hUFa*>e0ZwEROS{eRc_U*VV`F$C=Jtqb-$9MS)~&L3im zV)8%4)^9W3c4IT94|h)3k zdAT_~?$Z0{&MK=M0K)Y#_0R;gEjTs0uy4JHvr6q{RKur)D^%t>W+U;a*TZ;VL{kcnJJT z3mD=m7($$%?Y#>-Edcet`uWDH(@wIl+|_f#5l8odHg_|+)4AAYP9)~B^10nU306iE zaS4Y#5&gTL4eHH6&zd(VGyR0Qccx;>0R~Y5#29OkJpSAyr4&h1CYY|I}o)z ze}OiPf5V~(ABejc1pN%8rJQHwPn_`O*q7Dm)p}3K(mm1({hFmfY{yYbM)&Y`2R=h? zTtYwx?$W-*1LqsUrUY&~BwJjr)rO{qI$a`=(6Uplsti7Su#&_03es*Yp0{U{(nQCr z?5M{cLyHT_XALxWu5fU>DPVo99l3FAB<3mtIS<_+71o0jR1A8rd30@j;B75Z!uH;< z{shmnFK@pl080=?j0O8KnkE;zsuxzZx z4X2?!Dk7}SxCereOJK4-FkOq3i{GD#xtAE(tzLUiN~R2WN*RMuA3uYv-3vr9N8;p- z0ovH_gnvKnB5M{_^d`mUsVPvYv`38c2_qP$*@)N(ZmZosbxiRG=Cbm`0ZOx23Zzgs zLJPF;&V~ZV;Nb8ELEf73;P5ciI7|wZBtDl}on%WwtCh8Lf$Yfq`;Hb1D!-KYz&Kd< z+WE+o-gPb6S%ah2^mF80rK=H*+8mQdyrR+)Ar5krl4S!TAAG+sv8o+Teg)`9b22%4 zI7vnPTq&h=o=Z|$;>tEj(i@KN^8N@nk}}6SBhDIGCE4TrmVvM^PlBVZsbZcmR$P7v3{Pw88(jhhI?28MZ>uB%H z&+HAqu-MDFVk5|LYqUXBMR74n1nJ|qLNe#G7UaE>J{uX(rz6McAWj)Ui2R!4y&B01 z`}LOF7k|z0$I+psk+U^Z3YiAH-{>k*@z|0?L4MPNdtsPB+(F791LsRX$Dm(Gycm1k}n z#a2T#*)k-v{}p@^L5PC^@bH+-YO4v`l7Gq)9pgSns??ISG!M6>7&GySTZkVhykqk* zijh9sE`ky?DQPo+7}Vu@?}15_zTovL$r%h~*)=6*vTz?G#h|~>p(ukh%MKOCV^Jxa zi~lMP5+^-OW%Te@b#UoL6T1%9h-W}*hUtdu!>odxuT`kTg6U3+a@6QTiwM0I zqXcEI2x-gOS74?=&<18fYRv&Ms)R>e;Qz&0N20K9%CM_Iq#3V8%pwU>rAGbaXoGVS z-r5a$;fZ>75!`u@7=vV?y@7J;S;E#lvQ?Ar>%ao zOX)rc794W?X64tUEk>y|m_aCxU#N>o!Xw7##(7dIZDuYn0+9DoafcrK_(IUSl$m`A zZF1;0D&2KMWxq{!JlB#Yo*~RCRR~RBkfBb1)-;J`)fjK%LQgUfj-6(iNb3|)(r4fB z-3-I@OH8NV#Rr1`+c=9-0s3A3&EDUg1gC3 zVVb)^B@WE;ePBj#Rg2m!twC+Fe#io0Tzv)b#xh64;e}usgfxu(SfDvcONCs$<@#J@ zQrOhaWLG+)32UCO&4%us+o5#=hq*l-RUMAc6kp~sY%|01#<|RDV=-c0(~U2iF;^~Z zEGyIGa;#2iBbNLww#a{)mO^_H26>4DzS zW3Ln9#3bY?&5y|}CNM1c33!u1X@E`O+UCM*7`0CQ9bK1=r%PTO%S(Xhn0jV&cY5!; zknWK#W@!pMK$6<7w)+&nQZwlnxpxV_loGvL47cDabBUjf{BtT=5h1f2O&`n<$C%+3 zm$_pHm|BCm`G@w&Db)?4fM_YHa%}k|QMMl^&R}^}qj!z-hSy7npCB+A1jrr|1}lLs zw#c+UwVNwxP{=c;rL2BGdx*7zEe1Bcd{@%1-n8y7D4tiWqfpUVh-lHmLXM^KZShOH z*xFp)8|Y+bM`|>mg}p~MOHeh4Ev0_oE?T1n|HMCuuhyf*JDmFP(@8+hi#f-8(!7>g zH}lOHg#Nw(x(LkB`Q;g)oVAM{fXLqlew~t2GU);6V}=6Hx<4O5T!!-c93s;NqxUDm zofsXe!Q%wAD~BBUQ3dIiCtR4WMh-t>ISH?ZMus*wja+&<^&&Gm-nBlDvNS4vFnsl^ ztNpIbyMcWMPfKMe=YnWeIVj|?e>nZbwm$=sV@Qj@A@PE#Gnjlk{CGPDsqFS_)9LEa zuKx7=Sa>|^MiSKB?)pG()OoM}_%lx|mMlX&!?+`^^4bT=yz=ZoxWH_ngA*jX*IZcHOjb62dT(qTvBPn`2AFuL0q` zG+T@693;<++Z2>R2bD`qi0y2-Zf>Ao)K0f&d2P zfP78gpA6dVzjNaH?(M_mDL)R0U=lEaBZvDI4%DXB?8uw7yMJ~gE#%4F`v`Nr+^}vY zNk!D`{o4;L#H`(&_&69MXgCe`BzoU+!tF?72v9Ywy}vJ>QpqhIh5d@V>0xHtnyvuH zkllrfsI^;%I{@6lUi{~rA_w0mAm940-d++CcVAe<%1_RMLrby@&kK~cJQDXKIiybT z-kqt-K3rNz|3HT@un%{nW0OI{_DTXa-Gt@ONBB`7yPzA#K+GBJn@t@$=}KtxV871R zdlK|BI%we#j)k%=s3KJX%`+e4L~_qWz2@P z#)_IbEn(N_Ea!@g!rjt?kw;wph2ziGM|CPAOSzd(_Cp~tpAPO_7R!r5msJ4J@6?@W zb7r0)y);{W17k3}ls4DaNKdRpv@#b#oh4zlV3U@E2TCET9y3LQs1&)-c6+olCeAYp zOdn^BGxjbJIUL0yuFK_Dqpq%@KGOvu(ZgtKw;O*bxSb1Yp#>D?c~ir9P;<3wS2!-P zMc%jlfyqGiZiTjBA(FcUQ9mq#D-cvB9?$ctRZ;8+0s}_I8~6!fM~(jD=psem4Ee>J zWw&CJ7z{P9{Q7Ubye9)gwd`}~OSe#Rf$+;U1GvliVlhuHCK9yJZ2>_y@94OzD`#Ze z9)jO->@7)Bx~CeDJqQK|0%Pfmg&-w7mHdq3hENhQ;IKK;+>|iFp;c?M^kE!kGY&!y zk0I0Fk*!r6F59pwb<6v2ioT*86d(Tee%E1tmlfVjA#rHqA%a~cH`ct#9wX$-o9erW zXJEEOOJ&dezJO$TrCEB2LVOPr4a1H9%k<&lGZo1LDHNDa_xlUqto!CGM^Y}cxJn@x ziOYwn=mHBj_FAw|vMAK^Oqb(dg4Q?7Umqwc#pL?^vpIVNpINMEiP4Ml+xGo3f$#n$ zSTA3aJ)pM~4OPF>OOXOH&EW^(@T%5hknDw^bLpH%?4DjNr1s9Q9(3+8zy87a{1<&7 zQ@0A|_nnege~*7+LF5%wzLWD`lXWotLU4Y&{0i|(kn5hdwj^9o@)((-j86#TKNN|Got?9j^EYE8XJ}!o>}=@hY~siOur_pZ`mJW+ zg}Q?7Q_~bhh6s%uqEU!cv`B=jEp1K|eld>}I`pHtYzif`aZCe88}u$J6??5!TjY7Z zi_PXV!PdeegMrv48ein(j_-BWXDa73W&U|uQY2%u#HZ5hI@4>q?YPsd?K$Vm;~XD| za8S@laz_>}&|R%BD&V-i4%Q6dPCyvF3vd@kU>rvB!x*5ubENu_D>JSGcAwBe1xXs> z#6>7f9RU7nBW^%VMe9x%V$+)28`I~HD=gM$1Sivq)mNV>xD~CileqbUCO{vWg4Rh# zor2~~5hCEN)_0u$!q<(|hY5H=>Bbu%&{4ZV_rD1<#JLjo7b^d16tZ8WIRSY-f>X{Z zrJFo^lCo+3AagC{EW4g= z#o?8?8vCfRVy)U15jF^~4Gl{&Ybt92qe)hZ^_X>`+9vgWKwyZiaxznCo|TfVh3jIi zcEf?H`U;iFaJh=3Gy2JXApN`o zE=O1Gg$YQt6|76IiMNF?q#SA1bPB@dw#H+-V@9gL>;1mg+Cb#k1ey8`dvR+(4ebj= zUV1Z)tKRo}YEh@TN=$v(;aR{{n8vk`w|nNuHuckt$h27 z8*aBefUxw1*r#xB#9egcpXEi_*UAJYXXk!L7j@ zEHre9TeA?cA^qC?JqR^Tr%MObx)3(nztwV-kCeU-pv~$-T<>1;$_fqD%D@B13@6nJvk$Tb z%oMcxY|wp&wv8pf7?>V>*_$XB&mflZG#J;cO4(H9<>)V(X0~FRrD50GSAr_n^}6UI=}MTD3{q9rAHBj;!)G9GGx;~wMc8S8e@_! z_A@g2tE?_kGw#r}Y07^+v*DjB7v08O#kihqtSjT)2uwHG1UbSIKEAO<7Nt3T;R`YCSSj z!e)qa4Y~g>{F>ed`oWGW>((#s$zQGbsS&sg}^pBd?yeAN05Roe8> zT5^XsnI??pY-edI9fQNz3&cr}&YORzr4;sw1u{|Ne1V}nxSb|%Xa_Xy5#TrcTBpS@ z368Ly!a8oDB$mv21-kqD9t&0#7+@mt50oW4*qGcwbx}EyQ=zv+>?xQUL*ja2`WGq` z)sWi!%{f{lG)P(lu6{68R~smEp!Jy9!#~65DQ1AHIc%r7doy*L!1L>x7gLJdR;hH_ zP$2dAdV+VY*^|&oN=|}3-FdyGooDOM-vAGCT@@JyuF4C(otz>?^9!lR%m-tde}ePe z)Jp)zydtP%C02mCPddGz5R9NYvrS6)Bv$~r@W&cP5lLp7-4NrEQDN3%6AmXH@Tdfj zZ+k^}6%>L=d8BK-pxgvV`ix>w6F;U0C zlZ#lnOYYDhj4r)_+s){%-OP5Z{)Xy~)T{p`w1d-Z`uhiyaHX5R=prRWzg^tr8b$NI z3YKgTUvnV)o{xug^1=F=B;=5i^p6ZQ3ES<#>@?2!i0763S{RDit@XiOrjHyVHS*O` z`z@(K2K8gwhd0$u@upveU3ryuDP~by=Xy(MYd_#3r)*XC z^9+R*>njXE-TIP1lci2Q!U>qTn(dh*x7Zxv8r{aX7H$;tD?d1a-PrZ_=K*c8e050Z zQPw-n`us6g%-5T&A%0G0Pakpyp2}L*esj#H#HB!%;_(n z?@GhGHsn-TmjhdE&(mGUnQ3irA0sJtKpZ!N{aFsHtyTb#dkl=dRF+oo-dwy<#wYi=wik;LC6p#Fm zMTEA@?rBOmn>eCuHR%C{!jx>b|+<6B-)Z%(=lG{@y_@8s2x4Hym6ckPdCB$7NZFp_|El()ANXTORs zO@b$@1`3tXjEm>;bX)%xTUC>T)r6eTFtq*Rp*_?%C+fEzT##kVNH` zV}-lw6&hY;cyl5#RR-w!&K4e)Nf4noLFyjiAbKvP7Y!=2lRiRjc$&d?P~!zM@4!?3-vyqs zhm*63jiRI7cfruv!o=zO%H2cQ#o64%*4YAJ=xp~No53pO?eEA$`fR4x=^|*#{u3bx z1YB3OT97ZU3=ol)l`K!lB?~Dj(p_i0)NN=fdgz(QBu>8xV*FGZUb7m4NEbrA+BJ1O z%CPI+T>JPq9zpg~<>QR+je>?{g)rSuWpyCDcc2@rE8T>oNWPiP*u zLZc3LaQVEsC6emsi7DCL0;U0BP!SwAkXuetI25TYuCwD8~Z|M@2_ z0FaBG|x zW)FZvkPsN^5(Q}whYFk-E8)zC(+hZMRe5VA6GZM!beBdDBqq#Rye$I~h@Kf8ae!Ay z*>8BsT)dYB${E3A^j5m_ks3*1_a^uA+^E{Gxcgw2`f7jw8=^DG391okclzQA zwB6_C;;k_7OnwT<<5RjXf#XxTO9}jrCP+Ina|?UA%gFvNJy7HFEx9r{(c&yDZ9e2aovtJL$um8u>s&1k@G6# z-s55RDvTcFYZji6x+UMyCu{&*d4N<{6;H^PEF!?X@SqMfGFR}LYImL1;U}{iT!qnA zgqLCyvSp>>nS}|sv56Dnwxdo&HrZG1WQL_EkC!D6j)JW4Tv1yyqe&aM- zHXlKm;srQVctoDYl&e}E-P8h#PCQNW{Dg*Te>(zP#h*8faKJ!x-}2Rd)+>ssE`OS? zH{q>EEfl3rrD`3e_VOu!qFXm7TC9*Ni&^{$S76?jtB;*1+&lyEq_j{|Nhg&s;W6R9 zB#r9L#a7UU(Vnq#7asUx%ZyVz{CiVL5!CBl-7p|Kl&=g>)8e?z&u?Q^r>L@P zcB6n=#5Wz+@-j`qSB=wD1p_n<(NhAp8wa!IxDP?M&_ zKNcJonwpOS>a3-OBC9jGV@*WND}F8~E_QS7+H3ZK6w&kq>B}kc123ypkAfx`&en&T z+?U=!q?N5DDkt(2$KU;t^dR}IVC|M)pn@S)m{saxD4V?TZZWh@hK|C|n(P&eXLAq1 zZ#v0gPhHJYiyjEkJT~&%u@zLE`Lm!p!&-VAfk?eF{HN%PeV5S87-u3n;g}^R(OZqI zA|##x9SAAKAb!FSr9+E^(}_HX+lb+XLQiWF2UmH*7tM?y7R{u3(Vr<5h8V>Y-c`SgYgD9RvV*ZP{xBLuk-5sAcGP5G zDdk)Ua8PaYS-R*C(V(}4>%>{X%~yk{l3&El7iOz}m0Y8MAl_Qc`-2(z2T3kJ4L1Ek zW&^0C5lA$XL5oFZ0#iRevGn2ZyiotWRIag?#IT-E$gv92YXfp3P1BJxO zShcix4$;b#UM2o=3x#3;cA8Q#>eO8bAQ6o|-tw;9#7`gGIFVll^%!T5&!M|F|99EZ z?=t(Tag~g}`Wep_VX!|sgf_=8n|trl((YTM-kWDQ1U@WIg!~YjGqsZNOrayhav_lrw< zgSle+;b;p^Ff)tDt~?&TweI#6(}<3?Uw1@|4MvG2w}sQgX*N;Q=eD+(bJ%jKJ9L2o z3%MlC9=i-DKzXOun`;&7ZI$Iw?Y|j!RhIn*O`mRl2_vUnE*Rf6$?{IC&#;ZS4_)ww zZ${m6i^cVHNiw5#0MSjEF!NaQfSr&DbTX&tHM{Ke)6Pt9^4_Jf%G&51@IH0aA7QRc zPHND$ytZTZ7-07AEv8Rn%5+<=Bx1tWJSG_?CqXuJ99Zwp=hP2?0a{F)A8HLWkv z)nWbhcgRVdtQ4DpZiw6*)QeCWDXGN6@7m@}SN?Ai*4{l!jL`wrp_lL`bJF6HVAOnj zNa*fTj+{niV5~*O zN5NwHHcEed1knV2GNSZ~H6A+13`U_yY?Dlr@mtyq*Eutin@fLqITcw+{ zgfCsGo5WmpCuv^;uTtgub$oSUezlUgy1KkqBTfdC=XJ}^QYY+iHNnhYEU)j7Oq^M^ zVSeY5OiE#eElD6|4Haq&dOHw4)&QX=k_Ut{?Uvr21pd&diJ zB2+roNX!_7mJ$9n7GNdG8v{=K#ifQnT&%`l82sR{h&TKf?oxK%8RlG}Ia$WP=oQ3C z8x#$S3Rrheyw7recyTpSGf`^->QMX@9dPE# z?9u`K#Vk!hl`$zv<^Wl(#=J4ewGvm4>kxbr*k(>JDRyr_k#52zWRbBBxSsQfy=+DkvQ40v`jh_1C>g+G@4HuqNae&XeekQeAwk+&jN88l@etjc2U0(3m{pQ8vycb^=k>?R~DSv8<0tRfmLp27RlxR~V8j?ClC z)_B-Ne*s0#m}G~_QwykU<`~vMvpTlr7=W&w=#4eEKq!$muL_QJblmEh6*MUg!$z4fC{DBd*3h=N|lf1X7dTfqL1v6~_al z%J+WD;fSJ>TKV*mid$G+8eIjdfK%pu!#kkan;Qi>LK<0bn$?ecFn-b|@+^+OT=0nl zZzN%OUn9w14s`D45>E^)F8?Z?;l!%DF^oL|Yt!@m^V@3twFD@^D5$*5^c%)sM*sbi zk(RQq-d<^O7T8RfFwEK9_us2+S$&W1-Z3OR+XF6$eJl7IgHM~N8sHzWeuzxpB% zE9h3~^*;?_y)7i>a4#z6(ZQ%RaIo)|BtphTOyY@sM+vd#MYN11?ZV(xUvXb&MFg6g z=p`JrH(5;XsW4xVbiJ?|`nutpC1h*K1p~zS%9GcwUz0UWv0GXKX{69Mbhpcsxie0^ zGqgqzpqFAefIt5 zbjNv;*RSO}%{l!Z)c-Qw`A_=i-}4-?=swGSMI^E7)y37u+#O1^yiI2ehK4F|VMVkK z!hIFgJ+Ixg^6jI3#G8UbMwE1a!y~wFx@T(|6G*f($Q=e5na9eDt?f6v;SI;w0g-j% z!J#+aN|M&6l+$5a()!Cs22!+qIEIPkl)zxaaqx#rxQ_>N-kau^^0U$_bj`Aj28>km zI4^hUZb4$c;z)GTY)9y!5eJ{HNqSO{kJDcTYt-+y5;5RiVE9 z-rfg@X78JdxPkxzqWM?WOW8U(8(Lfc7xz`AqOH6jg!Y-7TpXRJ!mtM~T)9C^L}gSL z;YSLGDG_JZayritQkYm6_9cy96BXEf5-2!+OGf|OA7sdZg?o)Z<$B#|?fq|82c!WU zA|T92NDMBJCWHwuFa{aCfTqmu)kwClHDDbMnUQhx07}$x&ef5J(Vmp?fxerb?&J3W zEcoupee$`(0-Aipdr2XA7n`Vp9X;@`bGTh>URo?1%p&sSNNw!h%G)TZ^kT8~og*H% z!X8H2flq&|Mvn=U>8LSX_1WeQi24JnteP@|j;(g*B2HR-L-*$Ubi+J1heSK4&4lJ| zV!1rQLp=f2`FKko6Wb9aaD_i=<=1h?02JU2)?Ey_SS%6EQ>I20QL=(nW-P4=5mvTJ z&kgssLD)l`rHDCI`%vQMOV-yUxHQyhojHdYC*$H1=nrJKqFo93>xvB=M`$}Roksx# zRgV+d8#sk=v+tN#P-n?dx%RC(iv;9-YS-7PrZu#xJ5%k4i*8joRv1J`M_tOQR`{eV zE~<8%VC63sx|_U&{Bpy&?!~^Ce+CNv^T)?diyKrA zu^d&el}PFVWKFz9wkriy~eruRakPmmS0ZsKRiEMGj!_V`HL0FT$ zQU#r2x}sc&kxyY}K}1C{S`{Vdq_TYD4*4zgkU_ShWmQwGl2*ks*=_2Y*s%9QE)5EL zjq8+CA~jxHywIXd=tyIho1XBio%O)2-sMmqnmR&ZQWWD*!GB&UKv6%Ta=zRBv&eyf z{;f~`|5~B_&z17;pNS$3XoIA~G@mWw1YgrTRH95$f&qLKq5wY@A`UX)0I9GbBoHcu zF+!}=i8N>_J}axHrlmb)A1>vwib%T;N(z z!qkz-mizPTt^2F1``LZ#Is;SC`!6@p@t72+xBF5s!+V#&XJ54bJ|~2p(;ngG3+4NA zG?$Orjti%b`%<{?^7HlMZ3wR29z7?;KBDbAvK`kgqx4(N-xp5MuWJ1**FC|9j~trE zo`+jX&aFP*4hP;(>mA>X7yZujK`$QP9w?a`f9cQJaAA2cdE{Tm@v?W3gT&w=XzhbY zCDpADyRHQ?5fOuf*DrAnVn6BjADR2&!sV&wX1+TC*Qk}9xt8KA7}6LBN-_;c;r`H= zwL1uGsU0;W?OEez?W5HYvu>6SR+O8l#ZM+X@T3>y9G^L76W?!YFcytB^-`NyTDB=; zw421!sr`Wwopu>VDWNN>IN&RxE08d0JJZigpK%)p|Ep&aHWO`AFP)}VkqQg1S#TY> z(W)bm7duX(Nvry|l%sGs+Eudz3=_A0i@M47VtBp1RTz_zxlmqgi53tT!_i)(bad*R zt<1n~oT!|>QLmYf?YL$n8QEJ2A6liMI!hRY#mB@?9sWAUW8! z3#M&1`ZQmRP*o`jtHjbA78}!&iq6v&rlp|5&!}O}NT>|10NoWbiq5@7lhquTSHBCO z2a!-M+(e10feoq(nVw~!ZC;y+4M=F0%n)oHB7{BRYdVpeTN zryeS3Ecv^OC_2HcYbRWnOSY2McCa2PfRXH~!iu|fA^#y<&eJkS1^d|DM3)QKAnMe1 zp%9s~@jq$zOV8LQ$SoOZGMPYE@s<@m$#S(N##mh{yFb!URLo?VmR4c2D<_vio;v$u zEJivu^J$RML#dZFhO#!?D8s-JTIP{sV5EqzlSRH3SEW;p+f8?qW%}bdYNyDgxQcQg z)s4r6KHcPGxO_ErHr?P}mfM;FZE)8_I3? zDjMJvQui}|DLHJ=GXcz4%f~W;nZtC{WKitP66ONo4K<7TO!t?TYs_icsROOjf=!bP z#iDYw8Xa2L$P!_IMS+YdG$s?Gh(pybF}++ekEr=v(g97IC8z28gdGEK?6QPNA@g_H znGEeNG!5O#5gfi{IY+V>Q!Z=}bTeH|H2IGYcgh~!jjG`b~gGo!$<2(Kis_p5;(P-s_l8JWL!*jOOFW7(UIXj)5^C~7r z>g7M$hT|sIVBpur@M~;gi~j(BNMp8UkYv?y&{`-sK=@)-@S(2kqobO@Wt_pSnMh|eW*8azy%8exS@DAQxn9~G zE=4(L_gg-jHh5LtdXPgG=|7Xcq4E&x?X2G2ma(6{%4i1k?yUE4(M*Qk6_ z1vv$_*9q$Ow(QAvO;Y5T^gBQ8XX5ULw$iW6S>Q`+1H*Qj+COZ<4PxD-Fwh71j0cBx zz1pnDR}STs5k`ekB^)M`Iu39H@BwM@^8_X7VVp@epjNMqRjF($LBH!#dnEe)By}7T z7*XbIUY>#irgB@|lb)RRvHN^cPT%6slXqX1FW;4YMtNurd;?3g>rm zCSyAc0+aO+x0NojMi`4bp59%=g=zuk4R4o~hTUxxaj-YA z@UtFr6OY{A=_+?qZnrqBO49}q~-hZ!+0QZzD)8F6c7AMQ8Edl-y|d#R;NOh4ukOeId((#ChBKo`M=8Z@5!BZsX7A3n)%+;0Dy*bI-#fNe6_VV1{v%_*=I&54mqAWAg z3XmVyRkbAG&>7rIx23lx*caz7vL$Tha&FcrqTEUNZXhFsibRbc*L@H$q*&{Bx?^60 zRY;2!ODe~pKwKFrQ{(`51;0#9$tKAkXx7c-OI>j-bmJb*`eqq_;q-_i>B=}Mn^h`z za=K-$4B2-GE(-X{u|gHZ+)8*(@CW35iUra3LHje(qEJao_&fXoo%kNF}#{ zYeCndcH;)cUYsmcLrAwQySyF2t+dUrBDL;uWF|wuX8S|lr+Kg8>%G?Kuzxf;L!gZoxAqhd;`!i$5wZfphJ-c zd|uR@Q=cF4N1HXz1y}KjQJ8{7#aqNM_|j!oz6@&wEfq)8)wG4ngiGocMk=1Ft54#R zLyJe(u>P{fm>k_wUn20W9BZ#%fN9ZePCU*5DGK$uQ{GP3{oE1Qd^}1uSrdHw<-AM% znk>YZOU^R94BahzlbdB994?8{%lZ*NSZ4J+IKP3;K9;B))u#S>TRHMqa-y}{@z#V5wvOmV6zw~pafq=5ncOsU z`b-zkO|3C@lwd3SiQZeinzVP4uu+V>2-LKKA)WQXBXPb#G9E8UQ%5@sBgZtYwKzkq zNI6FloMR!lx7fV|WjJ*b`&y_UK9mPl*` z;XO8P%7{H*K=GrNF#+K3At?5`_oXT|Vz!Rh_05t2S&yd`A2 zjcyVJB|#czi?o<&biP<}0alxnpPLzJ9d#_R9(c$2IPXg7=4mL{7WoN>JTCCZ%zV{) zm691r%m?d5yR3l=Qxn7|f0?e7@ zk^9ia@dNTbyi6%GO;kec5sHCjtyr*i1QSY;G}gTsivUQRTG(i)y`O_~K{I*S+x=>M z;}<><>$k8!-=R}>b#)kmSE&~qf+xi@lJazu^F@~pV>MQ3ISq0)qH;F^;_yT@vc-Pr z390Cb$Zq{edB^7W@Mz_+gQ$>@*@>hJIjn4*`B@N%Lt_t1J1wT!aN`jpEBE5;Z|_X| zT^67k%@CVrtYeC}n;uLV%ZSClL-hu4Q5t8ke5a8BZ`=p#4yh?Xa^Q~OrJm_6aD?yj z!Od*^0L5!;q95XIh28eUbyJRpma5tq`0ds9GcX^qcBuCk#1-M-PcC@xgaV`dTbrNS$rEmz&;`STTF>1pK8< z7ykUcQ^6tZ?Yk3DVGovmRU?@pWL#e2L7cLSeBrZc$+IyWiBmoex!W#F#PlFAMT00niUZfkGz z0o{&eGEc{wC^aE3-eC$<2|Ini!y;&5zPE>9MO-I7kOD#cLp<3a%Juu2?88km=iL=? zg)Nm=ku7YEsu57C#BvklPYQ>o_{4C>a9C*0Px#k2ZkQ)j3FI#lIW3mT#f*2!gL4$_ zZDI76!tIw5o=j7Opkr~D0loH62&g?CHDg;Lp^HZ;W7)N+=s>^NuhmsYC?}lxS;sOE z69`R?BLA*%2m_L7BSZ^X5BKaWF-Y?b-HqGLcTd9NU7vY8k|j{O`cOrwxB2WW@tmhU zt`FA4?YCJwFISu42CLh~%e8Qg093rgqDa!ASGd!qoQ1e+yhXD=@Q7u0*^ddk+;D{) zKG0?!-U>8p8=*&(bw!x;E{EjWUUQyY3zVB2V}@t$lg*Bn3FId6V_Ez&aJ%8kzKZg$ zVwL+>zsp;_`X|m4RRvc|Wtejy* z?bG~}+B%y$b6zBRba$P?mX#UbwE{i{@jbuL@tZ6Rn;SCu#2M*$dpQIn$Hqv`MgjBn zURSnq5+1ReLXsI#*A8G1&h5`YFo^I17Y=&&1eQDtwY8HI3#DdGWslPJSP1` z1D()O()qzD6U~BYRUPw6gfc4Wx!am$yM#i~5MCmF8=7(q7;n3?L@7uuvn$;8B8wk8 z3>T-EJ5X9Z3@yH;L=9QFtWmzdE_;Kw^v+te+u`pF zN4&*o>iRKeC&l_{U^a`eymoog3(GY&2h;5vMyRyld37+7bW+&7tvIfrL9TpA@{Z

dy!05UMhSKsK zV1FiJ5SlAhkpcl_H0wRzql?0Qp5wz72o2cMC@utM(|&o0ZO_JpXr+N7l~F?Ef_02md^m|Ly|(EN; z%;)3t6SWt{5hgzszZWS1v^AU?`~Rctor7%qx@EySW!tuG+qP}nwr$(CZQHi1PTA*F z*Vo_ezW4q*-hHnl_8%)^$Bx*s=9+Vi%$1qr5fK%c+Hm4kiE$B;kgV)wam25w$Y7#k5$> zyB^6k3i~L_6~PX554`c3Lxx;&_sT;I^U92G@fS6#(Xv!B%;H3+{e)1R6lyU)8AK1_ z?@>F5H=sXG=ep;kDRZO_ofS}`Jus*Qp3`_V4v~&b-RQ=t8AN5H5{@!_Il~0 zZd!-aH=h)(7CJ&tL%%{P{6d_g=5tsj%S3Z!QxjrLdjoKmNP-zSjdJ!?qL(UMq38ps zjKSz5gzwhDFA;5md5yYb>QN)U_@8Xpjl4yw5065)+#MSGp;yQ*{%mt>12;$~R{eVV>o|juO{Z^ z^o^m@DOBrE2mm1nLgBfA(Wi=X9R%(1UYZcZJ!3;*bR^smI~6lyn`O4BOwo-STsQcyodVA~leg9`{=l(qDl@DCM>s+w`%S_q*PIjYP ziuHHuj0VVW1%+TH*lx9#-$^q&l)G_ojju-w{# zVs{oOc>_fcS51xY+19tN`;V~R0wVyuxdkS|t zC}~Gtu-UyA{H5~6*ocUWM)RfQ076mL1r zFVWV%zx!_*zk`5&dFbdq4nbWxIwAu=`+$V-`m<*-Z*mE2X|>OCAJVV;wlq0E$hVe@&x7V(!xg1*;%`} zxxBu5;jmZEH*e!Rj=Mz|udBR8BR6LiGoLWb<1=<14it;Fuk$6=7YCR&;F+%r`{S6M zP92W>ECy`pZR$Q<6n8Zw1|uh*M=zK=QP0b38_aX#$gB^y>EahIiUzy^MP1ct%UhZX z>FFLVJ=H`FRSq!<_DtWyjLZ6t^Nf|?<69Aj$U0*lrAJG0{t;t8Y^SKLacoR%3EXw+ zDi5T^PkjmJp7@B|$lkEwHHaQ7BGc$})@qNRqk4JH!(bgPM!{Mb&Kz|UGk?QskODW5-NCJ3`Fbks<}%TsOB+e{Hn1i7BP z(XsKkfl`r0N)u1VqaPYGlDxR3>%y{&vYaQCnX8AAv8h8>a^4<#jAhtfa;TdoFlN=?Ac{@Cdxj{YI z!kxobbr?~GU8JKwH2Ywa(#i=Rzof$nu?4-zlN#QJflTO^QkyarxNI<~MY1}jy~Jz` zBRwV&0+G01D9biQ4PR*1NiSqTXZB~NdI6yVEU|AiWJYA>k9G=*`R^VFjr{jhqZ$&G za0#huq)Mhb&8oR!jrv%;xRe@b&PWBXh7ATurhUY7yobngzP;($8b5g z9U{5JMt%fMp(N6ZVGsYa2p(#ry;Y&;GG(DG((_GrS%r&waWuX94*RX8>&x|Lzv8WCaXaWo(3FK=U@G#S$8kCX_R6q|VO;WbeXk~x zmq?NS+S2WfO|{j{dKy5``SRA!r+%)`DCW{s?8uZJW{-4%x}KJzAtiyY6b#)!fe0kA z)=W5C>X6ZLRFH_-$)Z(B8Hr}FD#FLGum2gRluDsrJHf$do$r!ORQqrI6~=-H0vPiG zC2V88MIp?Xhc&UnIS(c)naRXTu-r!%x0J;3uWjp5K%!b_v$;;T0*{_2txs!*+BgP} z%eY2;N7AFz(g@fFy&(hWk`R9#fRZ&X598A7xjHyoDJ4!3CK{Grr4>0bTBw3ps{tN7KqVY^)~B5St2NQS9wH_Lc=s8$1H5J?52_$nh z+rnm{F~bVIsiCZ^Gy&eV*X9JTJZB^`|6F$9|Fq@ekZKP~h_BWGsow^hUpo~MCTrdk^1B;= zNXiYAZnUPm>}{vX*&Yb&{0FNvW!V)h-<{na1yT-|kAkG7xU7QA-NAc|e4Nf2`OWnV zxbr6@^wO^6xW+Xdu=Z{sdK+Qw3Dii+X&Y(VdCv>CFEIOt?MCM?9@CDUKm7+N>%!q z$WI;(L@2YJ&Qfwr7k@<77r}%_q3O8c#><<+(JFdeT2?e+nsP4h+`n(HuX8^8qLN88 zv^9`|ICnNwS^PYDf7ebCGG~QNosD6-%$5;6Yx$`PGlZVnxs6ntftJW^L?iy3KIBDW&1q;{OspV)`a4w`+K45XmW5g6HLPL(lu zM^>HAPux}=ZJ?|;f=zDh!2|)WLyu7pHcc)9vAr(R_-sI`3GRfExjVpYMgql~xox)Q z)W3=WFT93oMdC)bluYO{cphI8Hjl&)W$TKN(PAk2r&mB9-)@%@xbewYx!c z{}phewJ939{qT;q&KR_!>>XnVYPC^kRaX%+G_v;*kg4g0jdi&G2G5$4#bk+*0mK8` zie_>y1oDA_0hGE(n`I(s0k(P&;*KDaX278vofbbNMZ-&1MCmPD*6d6oN$VjMzpTd@C8e zg81s83_+Y#T;duYQ%tXE$RWVk=@P5Z1VY<1C?mU)7?G9IHYx#rHCx1Mhb!ajXBoJ-rANULXqSAu0Mn9s%@_;uy-AOG|5#jDZ3j5dR7|< zR_{f>x5E@uRa$=rDD-yel$t(bf5=#v9ZWObAu%fou?4KkV-kvjmRiGX7iDe(Q)_^=>m}`2$#Xi#5CpJTi#5EF1T1mmPB}c@A6ou~a`>sHSeM4gF(ksh|DObX#Ao1r$Jp3I3 z-#zhd+d&)DO54E0K@@kKgxRB5%x&3BZ$OrawIi6~b_kN~$5G(kH6b5BD&%g70UWu6 z-ub`EccvhA2YleM%U@;V)N{Ixrkd0bjN}m=kn%!g%wE&P@WcBs>5NJ~t}y$Ar7F1n_=iC*<|&`C=qG#+ z0|)?s_kRK(@&?Z40!~gQHirKa2ua%+8CVNj{J7LD3|*Wp?EV9bZ1_j%PH`5U;9>aTZzwPD=a zXur{4zSk&)HrOFOmSK8ZKMHdg*HQk|a($OZ(0puje1K8EZNjPavWjhh64i-B(p7Zf z2g`IQ_W)I`lGa!LCabrDUSVPmGZbVX*#xhnAH|koEn~hs`=w;zVM^IEU${9oXf4C9 zk#|zrR`2_TI+u08MszOoi%H;viD}|x@Ax-{F_aW3ZIQHw-pT;hgNi%weuhcB7xt*kubK4fep+r)eaJIl%p9|sqv{M(E4lgwXe=HL2nYvO$$HX>QpPxqUn}WG zs*l{rztHOO@k5#cP%_alezmlZW9HCcT_;auQpbtV(Kh6e(9wF`C;OM(L&uqUaFglN zk@mRfKGV716J9j|zU-6W(m9pmEF&sbiZMv*M3~8lC~<@%sH8mKCL5zS4h--)TNbi$ zGT~m~}sa$tL(& zG_GBAe(+OZUY}-iY-rcb4f^fNZt_IXS52F^MC6>C?-IuOUttpxwVQBy0~D@|I1g*pQ^8D9@mu?5(kge3_GjbOm2G+7-z zkx`X#L5jF0+(b=RSgOE*XGFk$mF562Yft^UFH0micC5KNH~tfuDq*ce5Q~fKPyieC z9su^F5Df-F2X&FrZ1?<8uQ5h`uh~m z=&m+g_sL;h^%^JcRk%COiklbyo`Co8z9C%hj$&e+^pKMm>7Jt({+@)$DJbC`QjMHZ zi%3X-hLW4Gca)8|Pf3A1t4Ud8Gcj`ZNDE=lz<+3#C9z0jMR_q934+6jFXzJ$uCq~+ za-#O3p1hSU;tiKizC8=Mh@y(Ne3L{f0B?%ewopC*gCiXqueXVpGg9HaGK>hK#}F8++%^d7M6b=5@V(e#PAgrUnD^4)b1JPZ-PGNWqckW?kadj9w8b7f zp6l)!4JIwHtcBOekEW-B`yJ(E6n$+g06FFIjgZzz&+`UpKdgY-=lxNe1BI|=Cg;T; z?FYQs{*)^&tV>xbx0m~jf7l5>`+q#>!*0u^UJNZmE(3w>j|yNHB$#6zkjE;_0pL0S ze2gb)=zGHVUt5ge;3k7XmZcc5;mh=#z-ZobkM!xX0De$bw@9s|&m~zN9 z!K5tX5=4qA2sK|$bdVMz5etUdXN!`}2PL8R7qLr)Si} z!IONdCg$e~UlJ3u{n50K+;kj7SP&tC(^xDUbl{fdvL#ilA93{7Vm|&0)1p+nx=!XmT2qv6B?FjPHZV*SamC-ro9lXMAbWtsPx?Xq1Kcc_^$@r-YuI4|#Q?})HOyhMfBUVTIsc4Su?*`>kGqVs(0tbI_r0@mbv4tR&NZCQd@%?W!R_Br)qtk^~)!$ zd{bZ$2k_tV&)c$dz%vTer6*=naysJcAnpE2vboBzhwzL3ZZg^xE_1)_2eUw2B&FcL zW(!+zg@=0oy{=sCi##j;)Rn!Ty7I5A;QytP@}FjBaRXc9p9bUK6(&VZ!%ayA`L8Y0 zHgiu1Y%~0(WC8`wPF)OYDg?-xhpK#kN37I*3t$V> zeFT`E`_n>;_dQuVYN1PBmZ_}9TfEcl#^=`Abh1!Ek&ykSp^2 zUtg|J2l-(Fu4-@Z^fZW1~i@QYwP9Q9$d-lN6U6i%K#778wN;pE7`?CIfN* z4j%4F^H^LF6Q70%gi@GEB7#Kar{F)1=Hjc!yt?q2&-sWb^&Mo@Ali3 zYsI8ugwjs$rA3@sca{d2=a5mZ6PM=U7R~l1{udpZzpk<&^i)W$IV*$FUzyJ>#@G4l zunDZP3O}4G8=e2)DEXo;q|ooRSY*pQ@?dPnSA%LBmzMuh zj6iCX{hWsksbMQPykb&WEA^2^)4$ly11z>xG12rAj}?8Ft!(tswaOoNlpt=|kqrTJ z&?vxxBG>4bNn(%_w*|gVh^|*LD_=TzvKLX^EG3#)_JHhIOGSwPo4|0o#`B(-!+g_f zebxHKe=60kQz4i3=g8Q=o!~GyJjpp(m|JFSl$~J?ocx92m&&RUW=F?w)i?X8sjbbg z0+7xvpM&&Mvk2s6TEQh%-l$+wW+-wwx(yPsAW>CS<4@5r)9$_e^l&p0?yxh8t`Ni| zvkg20%R$9KD0hWHDff&(!UL3EXA@7RAORZg2_v!tmF`q!lSi%o$>srm>6H|S)B^2X ztV|vT66Q&WzEYv3LCrtL@fFVn_1u!3AIwvi9c5g^-LY)$kEOwFcdT%;T!@=Lh3b{K zJ5DKC5TfipAQ;Xelrj5>A z=_T7N`9+b0vmdY_zM3SwtpmRY?wNX&N^VG?5}z__+A;qz)l|ZX+QaujvNXdiXZ(V? z{OmPo1P@Yd;$G3ic^NHAm|1j%cIXFahDM~236V%gF?}nu9!H?ApHB?XA?IZs*m$xN z6e^ufgCQ0+_=81#=-f_IGbvy4Xizg)_Q^<)baO)G5(DO zgxn}JpKET9(UqMupTD8jB3cp z4G`IGH%ByG7iZ-QD?Esze`e049rA`qU8-l!$qPyeHl#z_q%CNdv(L)XI;?Ng4p}qk zjkLr}p4PA1I;7{Kc1WJp_Y!Q55JqK#sB5nY)=dehb&d)~g=roafxSw>Sbm)`xVXcf zG#`10jAW<8I#Nd!Q<)M`*0YE;dZ$(eKex&V5$dNnGAi-clRskp_SX#aKy?8;Y^RA; z@xEcdlr!iVGK@89*}AMBb@T}NL#V3*a00ErFr0GKMbDa2oQ-DkTV{N0Y_X9!nY1oWN1B)$PK)1Hfas5LPvtlH8ZL@g6sQ;=~> z=vTK;Y5TAt=ya36;hG?pES_n__RRVv!qlpCcy$N%vN$cm%p@=41Lzl*;2C>KsLXaT zT7L{$DZI@k7u*!SE|y2=Df|?99>gyrLB^ur~Y)vi9TpSJl6Z57d+o)lQAdh`R5kMGB7)eE`*Q;2G zQEcRN!Q?$b+o zUoag8iRTMmKuJ)5s&zS~S*B1~zU7tUT|q&h!EInBeZf#vwR|05>zpU0zRe0VWg5C; z+*3eGa6)oAS)jk-xN&bD5&{yx=Oh{=T<=akX4F4Yue*V0VM zkH4;7TLKmx%@)s6c5z_Q&5qaRX;$2vIP-ud)H84PAd0uJX*ee_AkeYKVtI6CW@W(9 z8KHRBux28|zpfOJu7mRVm*s z%?_&|3rLG%MZsk-XuimeAl!(zkxHX`$uQhJ=7%bztEXtmw!ImA{G>b$_T&F%g zFsQ^s?i59_UX8n_!c>ZltM6ABcMHOtRyrRBB3#Yo+AYyiYjPIXgd#0RF$%&xX*?+- zsPtBuy)cPjVkYkf31o50Tp3zUe-dekc|5FYz`%%l5L^>Pje2fT{!AGEHxWG_Yi|{!_@x>cc6%5SD z$ZvA==C5j@X;L3MCV!XA?SG9M0(T#83W28(9aS(t{d&siNAR`PZa(ke>q+Bbo82ut zvU5xmnR~F1ffCpw7|Fg1Gx@$)QGYDzf$|nfH3sKP3=Huhz#4)dH-ay~7cR-ML4hxY zJC3AyNh<#3hBqDyFFY{D#*eE*cnh{slzoT{|2On)ATR!sO#t-^ABA9?$(s~V<1UDq zyo>|Hc*Nrxk#`IYFkXaDTnoHWAP3E#`a^&-`SJ1RcPRHkeTbBZ&q3G_0==kIKNsi8 zPK+SND@w;5@(Jm9!|;LDkth-G0@RZYW&YJ3k={qg)_?xtrkih&RnY!V zo$Y^|7$WW_MlSzvW>1PbggdqghA-L1jCJc$kjxUIfuHEPj zLAS_=)=>DNjluF!EIspf<>8IN^gzw?ak~<)+k{ykeXo%GE=68f$Z;ZaxUAiN%zGF_5d-JZ0I9JZ*6=&gi*5l3i_WA7VrU|K{v|a zF=S?&Yw?$7*XrNDug-5bH}qO#ji37gcoNsG74BAO>OHL zJ+$W5wVs^^UjrNk2QiwyJ(aXP&FiHZNvXoDgPCs;lE0r3q^E zb1QZFSr@``4tbojlnOSCOUjP5QW*?2!?w1>p3YwB&Mp*GO3M*qgz>{jv{ak$b7(E?tkY*+R+^&>> z2dO%o%W=L!QGyw(WuAnw#oO{!I(8KwC|wq_y)<9lMxDiZwL#OlUU_DnD8&!tX&a7f zewQGgB8{dwkjR8EC%AP&bY^iirN#jA47*}#6?~g6@a?%^7(){yv(mgF=P`2yXr$Ab zuYEY=Rw^DeYTFZ^Ywa=6!`PU?q?O*FI=gFl`bbPev2k8T+=C;_X>sLJQt7BpOATpg zrpfyxa?;Uc`KUT2B@@q5dI0rCDDr{Q8d~En$h%e_rtAvjTEMd-OH%Qc7)o~}(R!O` z(i0MG6N^6LsC174qc^gK-0ayYDy1n5!q9mg_|@<( zH^wGhrdBV;Qzf}LA3=l3S|l{2(ylqgc3&K7pj~tzGSA`-wO86b&05pv_SO)Zw_hfmjx}wah`^|Qo(J(X2h!rc zPxx05-j4zshLMr@l7%0`IwPtjmgCwA{Sxj^m0H$vopZOcn-(l18gE{v?!K>bbY!=G2sL;OsI!wlS zl`om0y?Z#6@8vtXFRh`e5wNSy>T)H41%)Nt*jt9t?c#B>nBknI{Kbhq*5+Q8Lxe_H!J*!N? zH;Gr-bx%ExZEmt^9#)xcGN#!|?Xz6|l^~v7U7wM4&5cAIxbMj53pOBXW2LxqE#=+s zUC(EG;8)Odp&Rd)Qg_wrCnDExg_o7dmilm!?}lv0f5NK>w#Db7WRQa5Z94pw011GV zyHnjESKowJ&H%GT#al{iWgq|S`7S)99~4MXM?gl`=`rD9WWj$*)*NbWq$x&Jdq^ z(Q<+*Sx9NqE8$^Fqc(bfoIHwRM8##C@jW61>q;vG-*gk8G>_$;P+4b&%lQGl^XQpt z@48~+y!wp4mqN@Q?HOZ!Yr_;kT-E1R!Dz4OldNG)t;&2^&}q?~dMa&r60E7E)}#>< zrV*SWbim~#un~*J_!+nsWF_-x*9gTk>Hl>g2f7!ZQCMExX9omA0+-Fd%?Ek`^u5Av zTse2a$3`W_+4p=xIbdWKo>d*OlH=zIocE<>kNpS;Lx`OQ&-Q1P$CASxn1-0~RGYd=l#b>XT!xg+7u%F$Q7jSakj)eTa>Ty2qji4Eb4HFzvHy#qP|SXp zeb#Lbt?Nt*I~QuZr{s3Gk%GGcNPV5a16K0EjBCtb^pLdk4E5uLHP+1tY@v3z5hntx9$Vv0Tj2xkovNOuQz_TE%+7VTio)we=x|p6Zw6woNPx zcG_Z2O%BbGxfe9ld2ol=fLGR4aFV*%y*3D#mSjOJI|7z5B4+&ACSoxT&RK_fuBkxk z1Z{D-MxPSpq+f$DN!oyle^-|TkMi;fqFJ1UGd5NFA{AM^B_NurnPV??jj4yDq`QF! zXQ%rlV=SedtGKM5GccN+LZ_zY*nRh^QhVnOGA2jgF~DjqY%>eUXu}5pt)p9N9V|0Q zXC@$-8kj_9y)dSR&f2Q-S$t*V60-4m5IfeHAp)(*?%V*RU3YRI+fVm;XbrN;Znfre zHV>~Kt<08qOPU*d|3s=CmW8uaSX^bMnclwZa0*-JYD_xdlH-9QSVqCTFRD6%n}VS4 zy>uY+r9H8?BwSa;PMf%#`x7lDq2Ra&?)MJ=q&X-Vdw3kLg=AF;bh`Ngu`{SU0AP{2FA1bXzI)&Qc+N zQe2V^EkBDVUja~}gLyF(bfSN%OWm}6u4HUH3r`v7TIiEzS4!DYc1O$+O(bDf_b(zmfoP2*iYBPA-5lKMee z{!TLNugW*re`hye;8u`de34Z~ks!!LT7(P~?WfwY)j%M(rRlsVfY75wv`_j8-f<~Zh@@_No5u3lgB08$gw3J7t6YYm|-P>#mI z?Ihgih8w9<&jhN0?+L@xpaZf^v}|(+(B!Te$gx^{k_-y^@xZ8pvz4Teo8$&XcRy}gCz)E#b#7b-MxVm-OaCXYoKRhcAIJfQDELSMoUPZ2A zGJT9WYcGs3O6S~oE52|3o?hBGjTo}Z^#p~Y8HA5Pg?)uzq1dK9(?}wqZwRa130=%H zYf~z=E0yYqfTG0fyWBEMhY>h2^w4T@H3nLOIgGoExay2GP9=7H+(sF!>QtGs1-g&W z_gbac+_K^zlCn7G0blgrvHCKoOxX2B-RbMlZrJ;wg{CYdkQ}uH=vCz{^XL9b5MT@I1LRLBCN2G_*J_s4ZGh zWx7MbR#kfA8X5^2SsOa1ssX$FKr+_smpYMtr_8IC^|BTXp$X~a|@aOR`r7XM(DK=Ni-`62A>;$AvH z9_f{d2&YCRYk$@WOzak*c~OoAFfe6f@DJQ(UOb0(1s-V6+8}t zM%Y6TDbM(n0`0~e(Z=fVgsQi^OTtAv{cQHYLACfn!I5^C`4kt?8a_m$6 zbcTozSL$v*0uQgb2#l)xk-#q3kt{M?g;oWD0s&KKtKIf|mIluc_x>!Nn=F(UZhmoC@MLVWfWf8%A{!LJ-a9ibm(5(&roPX(GX)q zd@M1x1j~Z)riLkJ6l^njEwFgGs7mySZY8C9vkvltS$4KH+PxmEb7GD8$Z)quJ$36>!5YC6H4?tWLx3jX zL_~2klDHUK>j@1}T+ZgC#@^9#==euU-lRuP-UC^5Cc+L8jCGOV7-{#UL(6{hSs1p> z-8|04uLdI$1?;BBEEg_BTk#KN4^e`X!u!4==E(^tnRt1KV|!i-9k}i*QR9@it-?e5<6jq(E{}G5amY*n+H0gn_Y9 z-8;^pTZ~?CK_9>Yi%5S(q=#!=vps#u3bpC*N25|FGH$TQ9Pd_4r2%$YW!S{i=_C!G zD_fX}hHLaDE%xg_fp|i?KbzndD++)5bCZZKr8}JL`2AxVDM>tTh|-T>%j~EB_}}&( z|K(H^a5QtVF|l}x|sSOHm@dqAK_|9T*4ARfIiVq!E1 z{?^1IHFL*xX$M4a3Mm5YU!EpeD1oBkARcKhJu}}&7N2i-A0U4zc4~oNFEZ@*1*d{J z{!TQ-;$6U&WxGgOjF^lV^S+fK(41yMfFZe${01$COSKm>OdY0Ko`nRwC?nIcv5sS48^fobUN+7gD3h<@?TK=U zsq2}1JqYJDkDjs^)6H3!Y^(ni&NTu{w6vfAOZuc(I-NvUIA5QH9(Sk7D2hx zNiT)h!1lkZYyV}v{?Q|*B<@K93LuZprFU9Oj(?x*`7jTy!&B9yOv zBC(n=8x!WoL6TsFoU<~Hlq~@JoFJC(_I;+4<3?2gkpWZU!T~EWMF7v*q|26`QcQ^K zyY7tY=WEzh-Beb}LTZdzTqsr?>f%%?W^OSKq2qcG1lkqAukEF_zkk$u>XCWe4? z#Ea%vy>ICg-GEoSljel7W)-xQqU;Q+>#pyscZDYnsvo{+1MT9<8T4`~uVdxf?M~|B zynet59NiL z!rIjSxz;b%7{vy1l_G16WSgRE^<nid77&vHB`Hc!j_1F`ZD`0gi18)_8?o51 zU@6a|ci)iO?`1pg1#z@MGaRt#+VAApkLK*L@84Osn8n1p&wayu_RhR=UwwK_{XRd- z@_u3Wn-N%#fS{lWoezfKS`U=q7T4pO{SIjeFQMNZYxLGubs&kZYA-$P^!^hNiAC_F z(&Wq`HKids+xS2b*p4AAYkL|*f4oYA(x!rpT&_C7K;2ZG?{}K&D<-FkT@)`3VJ0Xb zH#wfssnie>s1svHRy7r9dzwfw#yY({tYB*1nNx)vazVXK$6z6(v#cyYmxjT(-pz)Q zmT^!`Ze~41QiQ(6|xf}+@C5ZNKgKywZ9F6&s&=xLzP2GjAv3Y0oF|N9sQ z)#f|e$7y6jIc&Qc}%ut}8+Yq?|zk-iAB&`7zddtXt^a zODQ(DgQqHOTe)pS1jRV(Z4SSYxFFm9bj`YffOXR_nrFrf=Pmfr^F8?NXDAH)RY_IJ zia@*!T}8>IHGTVN@d71~NRP5^{UuSEQBA;iP@E>vHBrii=Mt#3LM<}6v(uCW8I>pj z)iuPfGO41XkYTVm86?P+ZI7a!bu#F#q8E#ld66=_3qe5(7rwYzkyP1Cj<^O27m+O1 zqSOMa#3!)|Oi}&%<#TTC!j#90$`EUJWnuAw(DgEXbdGZ}D3-~lWKfV3CT06jARCpc zgW3?!cGxC<4bPFx>G2K|pQw6%H=mDNJ9f0i7Z9 zM9Op2T#uZC_CRl%l}%9a`x8xq0TEG6nyJmw%8@N+>W!pE-tgq@Th2AO(m( z5h}V(JEs-EqPp`)cKevppHePn%`Qoa-TTm}v83nfYu{=X)eka!5~;S>wiZ9KJjMq6 z>Fgx8lpK|M8rEmK1%a_jTLUsb8vpPoSY+$7N+_;3vCrkzy8E~s*E6qfhheM@ zrP!Wm9FgoRV70zMFupOPdouaMx%rka;9iusBffkukbq&Oa!Av$T*C5wgjUDJqJ6aB z(?h;NzQ4!^wA4Jl_hYZYcSg~3H}db;N0wk864a3n*J6lB-nb)I+5y2n+93^b!`=_} zy?b!&O*YX7-^{Ztu`4-1**M4EM4h_wU2-D?C}Aqy5ML7Yl@D#`Ppq--or&5LPqq_} zTx|N&G1%{D- z63FD%(!Xv4BFxTlU%s)bFl{J%a)l zqbCh9*g7WHB#?5O@r&ddY*myj&i_IQQSRbI!%jx#TIh8Iq)wt}a5M>>xO${;MLFTF zQ_O(@DdX&)d|+07Gko>hSrJy|%;=1|&mC?0hPHtn%4a35agZa4ED#_egj-4`fBqo0R#9mQ#BIn&i-6N6{L`Zvuc zhVM*t=AS0*G3(^>#-9WE*H7jAAN6DZVp#r5)s#1Ibo$Ty%9LoC$U%Pi5WROaGDy=C zPt+z^E_YxBba`ZMfei{n!7?uADyKFLcYluL^~1#!m1QqvZ}0E6J}Q3>QHVrfykO_w zv$|82jDqR3+Dr8`t0^fspZL6W?}Nb;in4>0ln_bv#S{!mP!7LHENN-l=~@%6ujbu+43{~BuZ zw^SLl6$KJ<_cuxbNb7Q!O0hDnWC6M4;8A_GNy9bkmdF>;M}Dt+#2h+{u6VQ^>0eSK z?k25<;(Ths!zu0AKiM3QGv1%~7fk+3?IroYB0MoYk(mh#@FSK8vIjI`ov_bH&I$oz zrLZYtsUQX0EBOWR#C}5l3RW{%Bo}~%2(30eRFFehtEwIkdu=PDTFFsev{oQPGaF9N zLO7CGqMw|o4 zXEdacLL>~Z9Q8;+O$?#CmfUc5aG9?YnHuPISSR3nZ8JM_D8dyb$SQv2-HWX?N}@nm z^pSjPE?!b&xN4pT6Iqj~IYUn!w~x*r*YJ!DJC8qDd%4PPqge{1d$*@GPtr)Wz z>kkUX_B@U^7XN4)%$HV&YAuDsY&6oUGVU~47&0HNr6)8$M29v4AHrT6Y7amNwe@2$ zMSs9J#(B)Opvkmq-rs#zH^A-}z<5I6p~|}zU3FOP#3gE}fPLjmm(O>k5}KVb$R=n4 zvES$OqRV_LtbbnFs2e-~T>F$+Tee&KFz1vD>C`sQ)TI=mBR(H3_R%|oh4VtiF3Lw_ z7tdE0!H=H2f)&ytAwMlWbDnuG(ULf9m*DTI1h-oaT(SX8kWAje29U8iM_5m`S?wCh z|2)fTcQ|>_y8p(TEt&BeR`_UPS^SO_Aw+z!Pzmz)2I2q4*o0Z?4L!A|{tFwR-u=j9 zsk_AMkBW&!9LF;X`vOexf?OkPMS?qF1or}T8%dvO4jne0W%dkm317^C;}z8p2F%50 zC&$arDGBdTWteETu7-Ej;`Eo6}jy1~TUaAs~m zhhS2-ZEu)clw!Zg9(sfvs-2Us;-4ssADLua7E|t`zlU(bj*`I2HTml-oa)BD4e;6x z#Il6qrF;-Y&tW8D@woFayo)8iO4hl9<<`}vd|k|mufrz)`$@MDyYyXLUZ9H^p@Jxe zn3mtSIH_Iw3x1|2Uhj^WaR8u^ISw=>@4vIf@UM=kjX!9O{)a6V`2W#l{>NGNfA8Xd zH=IuY-n}iVHvby@n;Z4Nh6Epb#M;g4i74tF_sb-Rd>-;(kwu z!RK#BjQOW9?`I~}#+8PwCNmj9+V$-8Ece{>&Gqh|xAzMwe+X%;d4~ahM4=pFn5%J& z@T0^41a(ePmuQCKNZXc45sKg7Sq99%CmTnsy4$U_RC+C;tYjWEXHr!g4%MNwS8o=t zU5BBC4m*jkf0GUk%P;RA01A1p(jYj9Vw|c~O0{}Vr%@Vn#JfdxEAB5UcKs;NtiXs5`3}FZBK{*S)g3 z$55~%jX_?tZ2!@XL*pbtJ0W!BhNlhcAlYmd__dLYu$LT3VyZdB7?{G*%+mk){+zJ4 zs;d!SlV0vINdFQ8yIDmbS|~){ZQ+Xl-0nVjY{WBZH5Ok(qD#50@k&HaWJ=SGQjG>sw?0g%xYX zo)I%5ZHB10EwcdHota@yKcn98pHZ*azYhpLLnCWD!~gxero1VS zp@{gsIoVg3UI+zeB3s%p_gfSf;DeNK@ONMnGm*)fS&4SKAx4v=6GM980?4Bv)-VW8 z#%=F+UKG0m8qZe7ZTAh#?Cr)Tq8}KQ_&S>Q)0X>H>+#1=Ija73_V>pJg^y?j*~!oY z-dh3EgHGCh#cwnQaC#T22>X=76ohcssCz$4SzkX0OcV~A(0xas~l-q|+(dlYU+po{VjMHA~h+?A9sV>Gg8pemGtgwQ5AD<1!^m1fsM?$4U=Pdx_dA z1Vdd^{^<QaRq{WW`$q8N+3kYCzjK`3k>V=-aI z24Nj-l1^-9@jCMfs_jjagNd?f30jHf$A9_`|w#Lm3Kw0)GM{<}zxR z>)9>F0>Hl3fVi{#9s@Nu0wh9jAuXw^`{pc}oS@tT^KC?^x}q(lC%Kz#g8xDh&VExs zNwY#ntAS8{_V% z>+5d(Cat43U!n=EJ35}M^%!aT7r^byL#@M=>I%4i#Ns}GAERjzpA-XOl0L$U&V?$O zU5Et*b(n1e(Qj=l+Kt#miKG*{HUE^I6ZIRiZkqVvq{2)w$2r|dfN{q6-d5PiP=H>y zFfj3n#fJ%9Wti#CMh3gPv`;=Zu!_H}OdwcEN1rtFVw`_} z_Z7iZ!2v$7Z1VH$Qo_SQ#Tns=?5 z`x!jNy9?0?NhcNi)A88qo3M6Dd#sE$?1>im5Hw1V3NN-b%$fzwzRli)mN1NdKEb(pdIM^yv_VSLm-8J|0?3wwKx390yng>H+3*|GL-*W zhqW^PVcIsjKMvvlr>9Td{6EOHk^L&Om4yV2S>uv;W9x#II$Ugm-=BcL6@dv|(oORY zX7m_FEQ`+Ch_@gwICp#EKsW=&-ti&EPRU}DiodxpG8l}z?0>$@*Qfn^lwUA4vHp>T zn8Xuty_)qK^|cm#L>NdIiWn4-tCFP#ErT)SiO;BWj^5g|5=@2g>;78mCz@MVas?|7 zTw9y_YH6PE62ZarIw}?Se;E~U6>#}oDb;e5%H*HjJ*!+#%z=w@6J{Q%VSe+1aY$-A zYiu2F<=VJ^sE|Gv9({JrR4pe`8$PwHv2b13V1af%!1$s2UkY;kRS;<6g!xUC8O*#Q-fj;-J7t=$q+gn)jXnj( z1wxL)j~-PE{e9s9bfni~T8*~RgP&P!!_c?gcR8}vTUg>9en5>d&RK=wqPzDm#gp4$ zj01f?E#o{t{#5aQ|3r&h{ZwH5!#4lnpFjQM4u=2m&Px?_6-;NO@5vh4aaz$4;+Vfo zXzFr0t(35F%ut&_KV4xqqT+;eWs@}=fuc#Njz-9FE@W#<@0CnSrHbWCOXB6BNkoY5 zx5$>A@1ET6XYn+j+&CX^rNsROBZnuWN+;2(HE>lR0 zdt+vO8Q`bJK=B4C;yF_|RX7V=U2w9SiCA@8{v$N4F98y0ULq4>-vfwx=hNc^ke)jP z=JtUX3@51;5GL@pCPIo6e?R{P_1Z&Yh~!3;`{l=LI!TdT+GBjnhRsd0E4$?t(cF!z z4~#=v5NNe=^9uQHzBg*}*h}OJs4&Oz+O9l{@=ma&6>15fDnS3Lu zhNjlUH_tu4aG8~G#M(x%^W-&-9c^k#MVC8F+(@<=A-S%`Ub$W?Fc$Kt5+9$Idch*` z8DPZGrrDga&I@4J#R*`!JUMdw*O>xdJluM;2O(QyC6bm(|7=LXtOMpeK2{Oc%&@VGgIM}n=xPTsHZu*o|%=ydsHI*DGc2AD4b$rWMYr_F+cj(?lYu$Y(d0;`Gym zsVB+o4{0WaVAxWNLo&g-2maMO*qGgJH^Fz&7= z2fEolQG2QIcl}C3QYX&n7uJjBQw?>=S+N}$3TvDBB4GzLg zRLYKx^=)OTX4DgErJ$67t1~NTT)b{xDBJpm-PJp6oYIFy>k5yf4es3Dl0RBGlcl=6 zkeqZGj7n2lOVEiD7>~>izlNL*I0?~Dk3B&I=?k3@VF&JxNNflsY7~FfIS1h??ud;d z(DEysJz}!|k{hFP%wR_V1vv6eo}VD6bZprUiHm6Oc!Z({ZoD1T7?|r-)XyP$bG-Kk zs+K#Tcp+0iFn)Ojr~N=xynz_nO>QaMQGRLk!77)=oI))vu#!h&Wy>uG*Xlp#{1EDy z%3$r6jdxpHLNJIgSmO)!3NMHED&BdX_<))Ch(?8pE>b8Lyn%w;OM+3lR+y?QTQooRsb|E)Y+ibYPpR&p z6s+)b!X(VTwzS7+!HF5!N~m_e9HxfjR~m1(1NVhmD`i`y54ph*TuOHuB+7D#w|bn^rs6qM}j4>u88m-909 z8Qn378h$ehryt=81-d2(punML3ZG(*KwecJa-AGkfNPyvMS%^{9mNgCm4!IL&HC@J z^l77MMF&_St=`G-5)v585Jn?7Ln~EA!8Fe_82Ch>P0PpQ+VT)sB9MB@HR@Z3(I;CA zJo(00bBCDqE0P=Q-p@S%iEzyp(jhvEEnkvBeitFmh~)w7kJK)2IQLuSThcG;t;19m zA}y3r+ik(BUg}RFoeS0@+Aw!O=T#}{7vd=KmTSobahGQvS@-iPF`2(zEWZ|rcL;+h z*A_P95X#6hgKb=iO8R&>Lx(@?U7Hnbcz{}VWQ+Y_<#T}WigYMJ>43m!22#ZMp5gld zvjS`{o;AuM{G5Q_d%Q8HaIyEgX^dy2Nw)g^$op4#@1uRb@iKc^`0oDIN}!Mz`O)-4 zeusYO!vEkuT+-Cu{)g`VLl%DQ1^)|Es7&0Jo|i!!?smr5TtY%458>ez*n}wn6hK@k z`Jf#NB}A3*Xpcyjt>2`!1o+JMh!McM?KR%_f7^?f=04Td*%F0@2j|n!kd%~Ws5j%c1tuc1<14SI~GT{=5FRz6U0JD0S?LmuiOd&*a4Hl2GA3j*mk~0 zHG{zh;!{+DZUTEyhhE~-I~nx~s|gCSu*A?HC1m3($CYe+6H9wDyGls11or9(nytJ| zd*-n%2D@K`5fS*rJ)?+*sq?mMo6t0*6fGywY7RRNIp4Ub#|f4Kahsq^&@5tt_sEw0 z6$tBs!r=*u#H5mic33oSM;v_oggvkemK}+&k^{?7?z2fqgf*5IzCiS_fY*Gr3UPfh4gBdXY(XjrTV_9xzp6snGzFWJz6*U5Ae z>b#^$8`}Oa>Yx%)Z5Ua^{d@1j`9<3&2(qX3VKiS|pK-r78?u0jI73d-73h_vE*v9^nb#_S=Y|+zY*z1#s8FFs5YJ2SHfgyTzIL#sp<+tP{L67dQd6i78rY* zPo1dBFRd8bfj;rLUm!egc@bm@LV0>{3_0s5RelFi_9kbtHD7z!KV_t9cYA;Qp^bbc zltWd_-A&ujR6b=W(!+E`0+JwY$>sB{$|=DQjq@`FVnLG&nzyoVm#wvk&sDJ%kUz$< zsz`N9uTKBzKyxY92j4VNeFI0ST2*<$kTnW%H&05Zz(!w3IP3>SMCedaI4A zV!|4#j{auL*KY|)(UQMQZG@D-G_i}_&nIGbPs1fosoM8gw&|v0gvu#GWiJny6dkAA z-tutWs3nWft)s%3*w5>H2Uz2q{mj;TB{`%`((Z0bgJ@|&bigU0=wieD!l+jHeA2opi z+<@NBOcX&dBF*y`WU)wDjBvt|L{|-1lJPd|sI&$C8(Rp_U|c3sZXHuWY9QX6;iwQ@ zLl)3S<^&wxggq*BjIn5v)~&}bg&vOc?VbThy}Qj`JF9KRFi;(X#(;=Vy)XB6dBV3J zDevR#SQo(;_9_)=xm+BwUe=4x19DusZ;98PG=+T`ysxWBjg|D)oYj_G%rpHZl7LV) zX$v2yquc{&c9dXA4Uk6IXmP8L=$*(MyP&AihZ^D6zu3_R{e=R?eo&(G zgA&1i|9A5rl>F<&q)_1>d>FMGiksGIAa&&UH3jzB36t8@&K8KuOPGl~Sdzxq8MLok zG>?S8p?u(Vy!;k|@2}?>b17=?6)Ue>Yv6hw&-f2<^6QYo2k0O#M4vuP>vh?m3~FAs zWF|jlFeAtn3PM((0JAqP$ndl)Z#OhZ5y~7=^E}9~1p_iy!7Z70a`oMBSE#o}pjLJh zVTz*5IIgH$C%LtC9E*RfOV079G@4(p_z1lzvA&$?%4XRKRqv;AP-^Pnu?;u+((h8i zL2LgIFjx6Cw&tN3x_U7nKUtE$c!a$9$#6D#qZGn;&uoa&U&%^Lp(&%yiJeB8xx|}Y z`tgF8XP6d)@q^wa%SeIAAnL0Rk7uuKv@%S~4y(V+fD5CQP@ZZivy)%ess1v}K?`t@ zQuF)fi}JY6u72#6vftxICFm+nwzg$GCg1zMT?(U0_l)Pc5!=B4LxEJS4ns<{gO;!< zXgw`8Hc(F_hbG98bMbG9=a+QL9r8@r^6nI{s-;H15v2MGagO#T9zUH9Ae$D7YdLjA z+b+6rUT1u5x61&npD`pu?-5155E}FMJ^B~@Z|iSJ|IA;1n~6ymKz||ax)GgDo`@H! z=P1HkG53^qWlx#xF?6NhQERNoVoC3Pkt;yj{nM9isXV40D1&?jp+)C!d0N7Z~W~jmsBwN~D`fatRBJZO#*%k>!yjFS^0uKVbnUJd2Ryq$#3wPIxJfZVqJ{k&L&9 zXGCBQb4AEn#6de{voh66ZgSnUtK&f&3VPU`{pLb@%fxrO3nm!q)B}6PdXBGvSNwRb znYu@N!ldSa(*GSjg59@YnmN^50&QLU~Q;g};bg&FW1uN-D6+(tiSj13|*jaU7szS?JO%dg{la; zsYTbJ>S51)l`=Ja293O0qU*grE{>~Vl~KEju8(CD)=RK6c8wXv=Ry{0eQY>gXHbMs zf(9?Q^CXoZo16h3k5t4ol0WgU@(59J#$rXL#!T$oiR2;)m5l~P=ou9rBG zKW3L*?Z8_lpgc$u*MB}N{M3p2H4S>dtnu8Y?ig969?)uZXiMBkgy{rwyvHX{IwQ*1 zAaq*bEdCiNur{67aksM~O|G6rDQ9Zva~!a|*~U!cX7%1NuGu&KR{sIq?_r_$D%$FK zxv_K6f~%Io%g_V7`)TPMKhqWVq~k!XKec!HEiArL`92$v=|=Fy{>{a`u^4b%_X}@F zaX=)3VSRhobHA_OLU51xa|m;}5)1(E>KAu5Af;kUL_1Q|j#ePnvNgw%f9VT`kTto~ zH}bUvD8g--TZr)D%6`~)z-4bH@U}GFb+C$o1;du}!_&pT=wTNZRcmcOcPPeBVAB6U zApYkL{b%<4&!DbQ;Zh1g7M80S$3itpF5HI{9ABip!2*Jmd?dIe6pq(l?`GSuohd_}1NBcI-LaLWPNMI*u862C=;tK_$ z(n&p`Ly#LKfE1kWXOo8=oF9Zma{O61Y#!*hdweURwIrF`@}}l=L)N;UYbO*a0={5B zQUPPZEY(0o5Osk`nMW4tB5m+6q$f&l_QhIa+@Wd8uwM`_ByCMc5C*DD%?Pb~C@-qq zcUh(7rHYZwlq0;NNurHgAibV_8IBFj&GvdPGrx4aFyXuJ79qf40_xr5Z*&bu?vUHi zrL{iT&VA80Zh;VY{H%tC6_8BZ({o_1Zv)FXq{4b}9w7xB9s!AIEI+J~1?*I0z!gqC z3xG=tIMJp6tvi@N)02M3zh-%m@oA)pc$rU1H2dNhDf8U~Nl`etmlVKWe5;&7d?}X) z#txXgpFv;o;ZgP|?+G}GT#aCqPZCeLfh~{RR&(0C1`nBj>JD@+Yd*Zipb_W7Gf&dR z5V2ZWykWs2WOT2WZg=R5kzfX%oX!y=y@3yCsa3&v#Q~(KRS0=IQG@~}1gL_Hi9MPT zOb$ZvS{D{a8pi$b?0yjmst@Cz0w#;kwov4k0bZp8{{js0aEg`EA7HHgs5Ad#3jY5h z$|y+wcqmZ4jM^{z+5*F5kf?I-8xU8MX!ONG3S{RC{6wKbw}R+RQPww&oWsAMXvhap zt+d>3e}@taRsYzaJdD+4Db3PcR$O_GT)VSUS82Aly#Lhr7-D^DHL6>UFAa!(Z`tDH2S}%#z)&5j#_v zI%kw=H*yBO2=zB(wjZ=7X^wI{0z0=}w?GQ@HU*|v+fE|{v@1JogpFc!`~(7k&3Q|dsgmZW#r!!e8PcYLjUy34;4uRDf z9#U%h>|eU(4V1H2NwYq^1oLj0j2<77JiF#IyodH-sB`399Jg_m`T>J$i9NBqF_T2| zyC&(TTyrJmb{i;KT(J-dQ+S^>oT@Y3lhjgdc2vlbcOEcq*0q?A*6wQ_9vQ>{0LuDb zZRZ6M1wCSOOxa5#T1c;C9jdqIy%R@%1LB=aqoVR=;61$~LOOqq4|2q|NfP$om`cza zxN$MGnK9`qf0*4Mo_0+=CIO(it+Jy|&3OL}#D@u}0H~9Qi!g9G0v+R!Lxh||kCi%P z(<{KR{57SQLKrXLIm6Z6l& zc$4!0Kzl;r(d}r&AQ6n@8xKsH{QdVC#Q%mnNLtVTh4tKLwY8B;`=gfQktp{QX3*lp z`jUi_(Lx+oeZBQoN2=!c z*Zn<;PjN}Bi2kG?u(|4nb8Qp|G&Vaa0zF69U4C+aLaW{18t48hLP};2qUR{TriE(( z_nufef{Tz|-WBOp)YCQ zAo-a9Tr1n4nZc&V?(4X#(kb*jw}?4Yd6IXU`Uo~-tv&3WlZt7X=AE&j>pXna8_WF7 zu%l%hY6M+wzY%r-KGIFb{7Rh~U65B(_(#e9GL)8hnJqlywnCmU+XCwELaE~6}7dR^0< zmG6o(Pe~FJK>Sp-LmmQ_Y{Ny|<%<-BV3k!?K4k7SP4Ui}8v#G&m)pT5%^uHxV*AOf5Z3mFX_%v@} zNJoU0h@y`^L0CQPfmGf{+kDXi6rb#B zHBK+?u?~L}H9l@Q&SWpRuHhg?M142jRAWZ!52aHNiFbvJ8aIyf!pst`fjGf5-6-f= zwb!bz9W=``d@FkoH4BPMZw#@XZv2wK9l1@uAviWs!4QCw$(cAyCaF|bC^_yq$P%7Z zu{nCX$L?(D3Z0;9JzjM5)QOA}SWlpp#I+9B9jRNo7%=6RC*+7oc@0!e*%D|r3Xd&G zl(~xANHEg(s8pe8%^PLPo!Pq5z$A2(dTpf|bb^>)2{CN|a^v@|NwKqqt4y zZJw|xD>_7omTcgs+u=xRHk>B!XurguZl!#dFd1?Y8D;e#LZ6?H0EVS0ayB!QtN-g$ zcH%6hKcDnOkn3A`eE6n7uz(m=Q__Lq7zgQdsbNhgsPy3#m~(CooW9}SsSp8C3pFuJO|^k466PtsDJwZU4jVD^=Zf6c$sz zJx3=tMkj&d{`&C7jN}vI;f;uc?!x`X7yFG4w_mUx-5YG#Gg~Rqd!M6RXb^Pvi z%t2y}>Hezt%l@$N_n%u|v#*jgp3)OuAYCVJJ)n-Lh+21Y{5( z{EQ?{{yV5!#4u$K;;=zlSwb&nd8J2pr6J!ak^wTk~#7Pug_Ji~W zzIeweDy5|82Dy0Q5*14Ejdd$Dj$?r03lnnPl=5km%95RA6a~DGO6YZEuqdOgUaFQO zu4U~)q1@XvD5O}+Z-ug-R`dp$p%jSwk9xHvD07!%0Tc#7cqp%hs;f4&p-QVcZpkl( z`ElaX+Gb+m8b%|Bzs)6CF9b07oG6b5{^&0|4*JL1*mI&oIx`Bew_lWCMGHW+^3k^T zMzNXq(UD+64Ee8TSm5)lC^r`p9Ug|pAbz()b%^tO2IYYLF!PBtzZWsd% zvISKmColu+(}g)1pXXz_g*7c$hjGX{Ga7|Zq2>!uK?&*K9$hJ&Et&?ekLm>0lfgUI z4MCYovgLTSV>!|vG=YIL0FMldJtyfX3?Oyt8JihgBD<$+&SSv@nW0}+4f^>V=?Jex zISZFs+aFnEzB3pEbC_uWhcEv`H8VLSZ#J!#o;EbI?WSGIwwI5GE;R)DF@be11NTRj zkL(pD$XEpP#a>4CVoAC8AxU(M|H*%J8Pc*TD%d;?W4CO2VlbT3e26X=rIpJMW)||t zBtD;=S4a_foJ;IY*+jQH0n*l_#f+dqI!IR5z`tP>Si>@8Uo<S{B0)7%2v-7I!k$kBpHTmCx3?f$ z-V45|wQlS}4y_x{$ax0I*8%XXm3rf9hzemc%s^*5MWkUflo)UxE7I_{PCY`gk8D7? zq}n;5q%8X6nvMkAp|ztEy>0Vq?p3_-m<;NH90_JLIdb`iwJGs})O^2~OaVug9$s;( z1TZ#2rV}R?B2&11e18F2sxI5*ZBPkV_iN@8bnk)$Oa^XTk>TskAA@lF)Y$Wlk=8bD z^~8Br&7r7Oww1+Qove3QT|**)gcG2hqNcwNmx zdKav4mfpGzC$czs#!CmON)5DFpNkY2Zp|nDF;s7?)6KX+izo--brmr3100TkLCV3NKFgNP zzRDHL-TM{8UGWvFl$e9gDvqs1tm7e8r(%k}m`Y@=_?SSB!g#1F`AJPqV30|!=_t#h z(Fz>96BCh@xDW?bmtWDKMo`x_sQAIHQw8-0=%M6^dS$u~RhUPwsr4pG9c@snMx#!v zz4g;^nRb;#+41L~7pu1BqmOog{Kai+aTtfhd#kjHA~ZLN2kB_bi;KzHjR#|?NgMbq zDtE4{hNCD4;Yl8%E#gLcPNNlK;#P_4h`pCd8+gw2kPiuIy;x?#P+wJDc1lF@JeRB@ z$Q|W*vmy&|?Fno9LHPW%3srylO;$JUqKUMV+^Jr}>;^sS*5lp}0mQKrIH+7jfcj1_ zg+s$)`O(~+Z5M1?oCRX%$?t%xb;lIl73z~;%t!lwX8%D0z6e`q4aN9(@%@&dO|W@V z;++@g`9#rU`e;?9(L$G*XN(8Bx}*DJ_pXYD$X;RIbq8Rr%D=?B$lobn(>RSrmZ>`M z-l<&a!zIsh8VZC13ys|@+*k?NH}m`AtVbM^IEkd?ryM$Cw+$2q#>N(Yi)YDlurNR8 z>WtKfeX;c>G{i;QZ0iQAs5v{=VT)>lsdThblcv*gG3QgFQq=PcL_cL3UQ$N(Nxf4R z4mK|YaaoT7B+@rRIk94fCa+#z8pbv>GA{?k6IfD9Qd$Y`8?O7`P8u?l8Bd@O1+~5F zk3b}KkS^EVpdSt0anCSL5RrJwt8hsKk+@l)dZiqBrNB~tHz-%_@?V2tbD~Rua0hn; zWoW$_b;r;ONq=)Qf5hY79~#b-t;BQ{x$wsnqi}_51Z!v z?L4$6bsRH{)NG@|>9RUTPPU;ONhxDMcV4ew6>^FOq?dPAiRxB-ce;+K97R*jDvO87 z%8ORzfSUXc=Fjj9(@u|Z<>=g^{8`_qMa2JjSc)TIdA9;7Ovs|WIF^2?5?@bHmEE9n z?$-A4c@Mu-|KO#O;O7Z`a9q zxJ`0HDXm>7us3bPC>`CLNegu8cx_I)SX5V?5VP5TcLnIIvESG{2TtKQ!ND(1UekCl zc7Z~|Rf=E8iPbjA*?%a-$`REL@!^e6s)e9S6@+6`78Q&|uy3@IdM-hfL5b}12!>@7 zfi4+{dXzwG`c-9RA($`Q=dT2GyitLcY8XS@vZwkO3Ci+XqErPHx&*hRQ>k!PAe-D( zKu_wUU(Mob>8;nnjzNB<#*tzzfAQ<1dwkKY{0Grhe`2(zv-PHPL9cVv!zUYJW6qGB=2E|tUuu!j*P^h z6A5wz`(>$mvRL93>J%R=#xIxH;;J2358v*)8^Nzz=BoGRGwaZ{3P8dA#muN~;kYDc z>n7*>Wq6krKp{owp7p!m9-g#sJ3KjP8~sZMC@ntYOMBxNs?=;(gUT<86<6XlZGIJq zmjh$mh%uR~bHRQ7BgV^SsjIB;v!HL`s&hF=eEGq3m?O6obVrt*UTHzU@Z4X z-?+ybh4+k#yoVF~sH@?!)5R-q4Q|Rswd5kTiVN*bX#f!fWUUvZ%G_8Wh_-8~Krz1T{UZn5L6|icUfS5@Q;jk& zVuJ-%WbUU5U_BeB_uF?JDo7x^y#3+W2V|U%!@mnHH_HruYy(upytxuSII3PphBQALx?9`yvjWq z!{rDyhWNr%9n&I}DeE;wT&`j5^IrP1xa2A;y)KY>>7rzO`p2Zq`2~9mCr27&C9Y}$ zfx-Fm65aMd-EO3PxIP63dL05*oaG(80iFDGhV@zm4jY1XbsMVt3-+Lk$CYS|8+hS& z8-%Yo2Jc~sPn4sx_K6vo)bL^3@`#>GdT8enLM_X2n`ng{EjEy6QHHDJ@!K4W-u}5j z;R82L;^tjjS9s~0wa*aDf%rR1PNM34(^t5xCC6U85Qv z#9;JkXR1$G`yyCjQMyIG)@UwUJ-!4f);oc9t_(w1yln2mwLz7>DA6+c{VHy#uD;PW zN?W=wE0W_bC`8(N-?(lFJxtjI;7k!>)4VR^AiV>FUDtB2%X2l;BD&j^t*Qr5y0^;) zw?b0Lo~#FTBRnG3aNY;OfGPz$bxA(;DSs7~`8HJMf(s=V$pp@Z>o_eid+dOnJS&Ua za40~9C)`k?Zi>!KS8xnaf9n^g-+oHVESv4eYS(du>_~|A515P|J4yDM=;2 zM0UyQN$}xOR(jHhN`2J1+j$tsogdDId=a1G34kCCB(G4k&=$@;>O>I|B>>^{_48Sc zF7goM;qdlV<~?UOte=}I&Ji_tE;=J>U=Zsh&qu-Rdjs0a+UHRgr^ak6plCe6KMeF@ zJU>)>K~p3`ao6e%LWVNsOi6dIjRmGE6I-(kifp$A3{Sw{=m9-@#~)7C{Vyvh&i?kDsRp06ZX^m-c+W=jeJ^p~r` z&+tq(N2?f3FuG>)h|bl(t=@I?$kxS)Nd|=ilsIL(qm|b|;aqq@BJM+w07*Q$e{p1b zO-~@UruWqZ<2gtf-?x_M^b)WpXI+Vm9hQZ_$sO<6#&`h%{5IL4!UqK9F4uw1q`lGK z{0=2%_apif(a-9CV}ppmK!6k0&h0_%`)R_3$Lf)y<^B~YGbDr6N0;I?p&eL8ihQ+5`uJtvS zwQtSfbOCxj}B3QIBrNu;DxC)>e6{U)~!hCzoqNp zny3{~n|&&G;_;E;K01dODI8 zgce24dlcM~M_7Q@}Ut2iC8q15dzD=iGf1Qb}_RWK_mU~xGb!Gi?!VX_-6|Lq=cFf7%4eVe=NU9K=Wtel9tQbDhyk7@)G zaj0%HnuKM}X@kYq@wq8P8UR1P)|Y09o!s#I`tXB|@NbghgAV!lkM0-Gs6jjMIJD5~ zLTaM>2S^zW_=`bgY{)EZmpg5NLtngzEc@%fOLn^h?{04}l=FyNQF^+-l}ln;N$hmK zs2B#P%)WyHu$muQ{niPwIQuM9iJKo*_bCE-xZ`Z`Ay@{x264);+4~-3-OIP`T-_`# zcPeW@wg{)zN6*M}nuJ;(iPbyb|6*;C%?G9x{IRt_{!DECkKr)?_lU;ef7!wRXIhh~ z{OXLMjPxZGE}TT-R6%H#QB;~Xm}EFe9!XYu$?iDUVr#}hM9pkPMw>)@R}d$J6`8?0 zlQf6iR@+cvy2>IC8e=EIH=_Fr1?>&keJd>^B{lK96=5)r-aH_DJkfsL)$Vn@#gXs5 z^)|2l3$yQ#bdR)*R1ofOEmCKVLP9=hd%Cg0imbqfWFZuEnWf4A+bwIgp6Fm8DZ5NW z9#*z_|FNv%tp!F_|2^DKvo?fmnI~PCrHkyKxU54iYVWw-r`#WH1%;I6#AaySpFu+JAajI9B6z9S6suF{--a*iU!GEB`hCyV+7663v!t`g(2DAf^( zvqL8QNtR_6sWrH?nM7C`d^aC+_^@#|yt$va@g@GW)5eal`&80|=ud zy3H!oR{ftWnPfWzqfu6(PngIVY4=rTa-mUM)x;s0BB)^ecXT%Ht3tf}4*m0dr!KVu zHuSYNA8)lLcAv_i3|cY6Gmlf87vpW zgQK60L2h^GY9g%N=dM-xTG!K_Ac~xyX35Q)Ff>57LNZBXOgcjz2f@}X4z`BsMOa+#jN$U=Mv3JwNnzIQSVcM;*Z3^E zA{w3pwPu#}T&w5q>C*~S!>Ck;QfkE4_@~-}UTIWF({*R?NVbKF#Tt%?4oqa2m1%() zy5ShK6#7M)xe0fFu-=Hz<HZzOA9QOVm*w#3~(}3Db$((Bg$sXXoT3D=1ov zkfK!s{bCbgA!eie60>QMBl$du2R;Ll3Orz#P0szlxIga=FiAe;RxOO3j-ZZT+Q5*? z6Q|eE7B>era5Jggs7a`%P6Eqn0q!c6Z}Qx?#9q-qP&^E*n=zQ71Rd7O)>QQ;5D{>< z2$yN_=V^VeVH*_*rA`uoo|=OY-_oF8)MjR)Bm6AOLGqg_X~2FldHi{{#Wi`MrnVzD zalyDY`H#%&obRVPCEA+Q3Z{==JPNl2U5QKkReQteUVho+E$bNh{-J=04tckZ#4b={ z#YfY19!wIu2|?Mr#~!MdwAhG$=D?u3d+3Y#ql3UC%v@ma(Y->Q6+guK5nSZ@t8GPl zx0v*OK4X_58bPD7r_r&0b8Ke7bAga^g~lBc+6|!@rJbWB4|#ay?>4(A_g~*E1n;i@ zK}pYZg7p5CMF#s2%bg+NMygbkP)>)A8rmWDUoh6^L%h% zUUA?NX=0>Bf2xpSkG+4hsathn7-sQHVo1_lFx>~p=JvevkF4kt|1(jzakgQep^wom zfv;MAa8fkl6)X+?yXVr&KOyuO2y@d*%*(WiWs2?0ULdr`zIB!l;Q2S1<20 z7k5(g7f7pd_44zx-869ZHB4^e`7ds-q;y|P;N;>sldO2o=P!Jawe8~XL`#|I-*kidTo?f;>AJ5z^yPW zL_Yy?tCFf_94%n=(yi!hm6D8JwG0Jd^AsX>tTdbR>88;CQdLJ z+Iljw44H!snRV~hZ+`*L@|C{R2I#7>_C4}O(DEM*Z}R&T2-zmMU=mc?Isr*%;l2Z6E@GdQXQ zE6yFGUdVB+48dw^#eF9P@tRto9xXw7caarv>W81sy`xkBCuxLSS zJYB2+XzL$#8wSySDztc86VU-1jzEqUjNycoV#A3LHku%J`m6DjMA&sBA%70|xj?F> z$%deE3^iWo4K}dQJT1D^^_tdz*`(?FuPq%TL5j8}E2Sgk6A=q77Ds1ZK30w{YP>p& z#8Vq#UY6HzAXjm1xJI4Cl-el^%?p2>fy%Q1LhYK1u%WXGg+sMSOM7{D<9fHu zb+yr%#^ebn7uVIY#S~TK9&<jqK}aJc*IBTk3GesKj0%hEbwuH<+{l)@|rc5 z-GAQ-{>shxYk_GNTO?bgUxJQ-v*(hd_CtaB7b_}5`75XJCbf7RdWO2IB<%VdjUhYJ z7abavE%-q)IMZ(_rXmIk8F0$b2D^fJ^0L!SFQ5mNFGF1!vnRa4I-tx|iXn0K<@piu zn!I_Zc>>#8+J`5P%s$me=Di=Bw0FgqGs=|<>MNzw1bHV!z{tO=ts#3LXvR1i7b-bB z(+XTuNJdAmk#H8ahCAUo5Qv$Z{fbN`t@EL+^l`ZQC3gjy8wnWDjeoZ~-X)RmQva6+ zAGHTbjm(R?DsQ^~dbshIIZMyjaTi`&a1+4*v%>4I+w4}F5KMetKAu0j2ezypAqt?~ zIT!PzHOjTgtiStX=)^XLORSQ-T8qwJbKZV^5`a2_Gx?9e%J=f;XO4t{e|#d~(b1GJ z^$Gx@Zl~deLFp61-Us0Gwc!6HhMq<4J6Dn~itURCUOqntcF|)BJI97<8wc2{_enZy zpQYA?u{$78y*U+Vo3?EV&0iyA3X^e@^)cYW-}n9(1BqMq&0Wxs1(oS1R!Zdmh#os@ zGedoc|34|qg>mCjeSZ;yrfpDU|J?f7%CZ25%mj+lgz{;?5%t#KjMYM#a!k_dxKL=O zw%h=CknWQy=-0?1w6l62Uw>z^%}<=K-$VSu?AJn;lNsw#0&Zfci4WRjOh7A;3M6@8 z^LHs+(~mJ31E3#i4h&vKXpTNhdd9K~voy6W9!>;Z%1xc&r!$%{6E{rXI9`I4OqQNy zxJG*RRQSJ2I}>;)w>OSYhR9M~LZos{lo*6aQd!12G`6~;m}DQuPLfa|WlLRKT+1|B zveXroREliLTFIIgd*oJ1uD}18D_+jkpnH6Ltk3UzmiN5pJ?FgVd8qGL{!Dwzg4I zc39+X9C0Lx{^I$>^PQTBw{Rf3>3_1Om{>t(y9z0b^~)7bDnHXYu{`Eble#U_&d!&& zqO0muWxsKCv7awPsWYwfe3b6hW)i9BW@9*n&ud8*nVdYs9=}KKc5lSZ*Y`aF(3%ap zE0P%VUey^Lu(i4%-Ej2%ie^l4si4mG?ef)m+S?0RB6Dg+JSu{nl}^7YYktIO@2mXg zk6v{~eslFzn0gh)_}|ncga~)ueQfGhocpp+;sA$J2xw~&(AF9YwKW`wbJkP_az%>tbe^WB+J|Mg2}58P`%3hV|#z$|=ikYS{X?2i_aoWVRqrw4GpRmSYS!x-AdZqF1dN@&?yW(6tB{}(slgRUw^dojogkv5-xylMbrrR#(P?LBG6U_1d zQ-8r#_esbnGGsqz-4h|7i~gBpB{xT3sAEf?O&#b5@0H&NPIZ((W9#CKl(AZR>XME` zPb()$5P(&J=uEVS-MZpoOfkqk;1$&rj&6sb^2G1b7ka?Ij}Axx}kXn%#&Ka~=( zBEvbvGPh3#IS#_E#a-6As2n2Z8TwkqN*zO|#2W&)1eLqCc(ck-Ndj;4+eDMHIV!@E z2`}z$+Q+u8`;uvWxbY`D(P8UE-9Rw>pa4WEPe**>A*Ffc}-k zi2sj41}83Yj_aGWadB=UoS))DMxUQ;iFq7o#;?R<_pkho;(Z-2L8j8P^u^D%f+dPG;UpB}sTa&=$IoCtP3saye==&j8<*KzwMwDHF+b<+pKzqR{Y_P<(F0mwn zrcl;zL6KVauEe4gHDhPT>Z@l>wLeSVa>1q*r+G8fesLU+(e^7VMd_Za%hk|*$~GF3 zn(%p#^~OgrCASlWg73E2-_vMibv(SI?cLZI?rTqZtAZ%clOC0It!$JlW0yQ1n#S!g z*z@YiP5%vnB#(n^Cz#oLcZFs+q^eM3S-;B$08#&rD;RZ<<^bHMtZmD^iqw zuBB65e^pB8LmvG%aninJoT`EGDyKd=Wa&3AYvQlr4>f1xEy1lR(5T+zoBBF2uU+0g zDv*2a$^5ln%`9J`F_)uF_lEA&znh=2`?0e2I!uhX68b>eF0xOMaUf^1X~ue9sF|S;^NedDo+GnDO%C+Gy1zg=|O+5EmS8KfwBxOGp^YhWZl9LB+ zoWXCn6}9=cTl!D|ka`B=OG1C=u5GOp{kS!4e_KL!?fWQ3@Ge#H@5XwH z8|@}}^H&;Lh*`Eq-rHN*GBln$7*!&cCq~X4tGQ10-EhUmc2~V$442}#p4}EhN{}hO zt)h1`@j%<93zx6DSiUeHVsA)enh?3KU(twm7ct2hzoFi8Fhz4PBbR4oFYZ&Q$;dT> z!C3D0%&p~^eRAO~HLXDdSN+63B{Q}9X>L4NT6^*ZUtz>@ANBO)j_s3mRYP4t;v;y1 z1J$k76io@2(v=)lQ}ui_yf*ydMmBj?=0@)9wY8RMTQft)j}b1B_xu07p-@NTt1O1- zrP&glb2U2-`-Q`(;a+19I#@FcwNEcG3AfmuF+c=pxVoPID8#uB=m8}g~n(O(fV>{k-yrT z%?ghWQ)IKh$vXwJZ@YAD40G=ap`+1KK4p)Br_1Woavo@T^m<>PC&B#hU!|J&ey|k_ z4nD3pDDgS3(P11-Y$uQNhZVz5N6F>F!h6BZllEk!_MdK|&aPx|cXhY3a?=stT8Y=e zON`*J*XWAt)HGrxwZ*q+Vqa@ZR!L$}q20V!284MwiP%v31Gsxj)?B>8!)?>u^OApn zubibAoVP(51dG%rOn3B)1%o>rsY(~gcHxBV%zHNcGJAG5LXzusqp zf6xIB1mL$bi4w3Gd_OZ<=ql@JspAZdBy`p3fx$rYJ<-5uph=7HP0s?jFr8%~{M}+| zNTO>9R$pfs>diHr8rccBgeCIxUk5pYDmyHW0xgInO29$zSUV$u*HXpl8RB4To$Jl) z{=g^)d?NLZLQw)fbI!8X+h+vqVdLNM)J_c802p356&!dPP6 zCE7UwrwB-(Cm67|{rYWDP!Y8AfYQ_I;43A7XB{1Ynw2%tgXFFTJT;NX#G{D6V^}|d zVDJD7^jm?x;T-)4a6Qv{?DzgRb=^((gMaJ8lLIg#^ggES;cg28O4wNB&wi4wpM0>1vR)_@;4cOr@Ob#+|3e&Q7EJv(^^|?+hTO*&u!_h2Ss`y zx5A)}f$&VC1c<8AQN@#OY^LLn!S!0&Q*9~*T1_5YgpxCYw2a=t(UH`pO*9TnO)F@Z z{`~n3`;;u525tv@p!e>cBQ9@1N1Q-(w^ep?vvNE_t6@CZl1Ngs1HH`dhzAnP1TKgR z&x+=ipcT78VZ`UK6Yo4@10Zu1dFQ^1lLKX#%I7Y+9FjbP)?{2X?wBENh6hH0t!iov~!_g0%`C9z|%z*OpA9f0PuiVfdgO zf~Mpy6+QnL1HT-G5DZEdApC1jdVT`D&y5iJDway1HzLD3f(U2xlZ7~o-yeiq2;Q4Q zs9aAMpu!K)v!10Ec)Wr4NDwHhZq{nR)NJ^N3n_D#JihOkz~zHi5)l;c*?&PH>xu*& VCNKd3JGtOvEm(5t0lFyE{{i--k}m)N diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index d83cf1363..423c23e5e 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,16 +1,3 @@ -# Copyright 2013-2023 the original author or authors. -# -# 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 -# -# https://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. - -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.0/apache-maven-3.9.0-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar \ No newline at end of file +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://maven.aliyun.com/repository/public/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/mvnw b/mvnw index 8a8fb2282..bd8896bf2 100755 --- a/mvnw +++ b/mvnw @@ -8,7 +8,7 @@ # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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 @@ -19,298 +19,277 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.4 # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# 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 # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi +# 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 -fi +# 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" -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" + 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 - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + 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 -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" +} - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - 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 +} - saveddir=`pwd` +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - M2_HOME=`dirname "$PRG"`/.. +die() { + printf %s\\n "$1" >&2 + exit 1 +} - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` +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:]' +} - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# 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 <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.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${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi +# 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" +} -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -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 -if [ -z "$JAVACMD" ] ; then - 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" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`\\unset -f command; \\command -v java`" - fi +# 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 -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -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" -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher +# 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 -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { +# 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 "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi +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 - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break +# 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 - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` + 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 - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" + 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 -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; fi -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - 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 - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi fi -########################################################################################## -# End of extension -########################################################################################## -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f fi -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 1d8ab018e..5761d9489 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,188 +1,189 @@ -@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 https://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 Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% +<# : 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.4 +@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 -eq $False) { "/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_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::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 + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -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" From f4fca6900821f7c222f0fd6c649bae077c26e67d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:43:49 +0800 Subject: [PATCH 032/438] Update GH Actions to v5 and use Maven wrapper Bump GitHub Actions steps to v5 (actions/checkout and actions/setup-java) and switch the build command to use the project Maven wrapper (./mvnw) instead of system mvn. Removed the setup-java cache configuration; this ensures the workflow uses the updated action versions and a consistent, project-provided Maven runtime for builds. --- .github/workflows/maven-build.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index e6edb1a1f..b2267b040 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -22,17 +22,16 @@ jobs: java: [ '8', '11' , '17' , '21' , '25' ] steps: - name: Checkout Source - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup JDK ${{ matrix.Java }} - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: ${{ matrix.java }} - cache: maven - name: Build with Maven - run: mvn + run: ./mvnw --batch-mode --update-snapshots --file pom.xml From 7cba8eb2a51a1a61768ddad78de1a66874971933 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 19:43:57 +0800 Subject: [PATCH 033/438] Upgrade GitHub Actions to v5 in Maven workflow Bump actions/checkout and actions/setup-java from v4 to v5 in .github/workflows/maven-publish.yml. Java 11 (Temurin) configuration remains unchanged. Update brings the workflow to the latest major action releases for improvements, fixes, and security updates. --- .github/workflows/maven-publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 6d40bcd43..61db0dc6d 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -24,10 +24,10 @@ jobs: if: ${{ inputs.revision }} steps: - name: Checkout Source - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Maven Central Repository - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '11' distribution: 'temurin' From 6d4df332b95e76b29f554807e8607a5df60e3eed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 20:36:25 +0800 Subject: [PATCH 034/438] Add UtilTest.beforeTest and super calls Provide a base beforeTest implementation in UtilTest that initializes testTypeElement from TestServiceImpl and add required imports (TestServiceImpl, ReflectiveInvocationContext, ExtensionContext, Method). Update several test classes (ElementUtilsTest, JSONAnnotationValueVisitorTest, MessagerUtilsTest, MethodUtilsTest, ResolvableAnnotationValueVisitorTest) to call super.beforeTest(invocationContext, extensionContext) at the start of their overridden beforeTest methods so the shared setup runs correctly. --- .../io/microsphere/lang/model/util/ElementUtilsTest.java | 1 + .../lang/model/util/JSONAnnotationValueVisitorTest.java | 1 + .../microsphere/lang/model/util/MessagerUtilsTest.java | 1 + .../io/microsphere/lang/model/util/MethodUtilsTest.java | 1 + .../model/util/ResolvableAnnotationValueVisitorTest.java | 1 + .../java/io/microsphere/lang/model/util/UtilTest.java | 9 +++++++++ 6 files changed, 14 insertions(+) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java index 6fa9d8130..e13704df3 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java @@ -94,6 +94,7 @@ class ElementUtilsTest extends UtilTest { @Override protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + super.beforeTest(invocationContext,extensionContext); this.echoMethod = findMethod(testTypeElement, "echo", "java.lang.String"); } diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java index 6e4246323..cf1e85c0f 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java @@ -50,6 +50,7 @@ class JSONAnnotationValueVisitorTest extends UtilTest { @Override protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + super.beforeTest(invocationContext,extensionContext); this.jsonBuilder = new StringBuilder(); this.visitor = new JSONAnnotationValueVisitor(jsonBuilder); this.testAnnotationAttributes = getElementValues(testTypeElement, TestAnnotation.class); diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java index f91edab56..554d4b4d2 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java @@ -46,6 +46,7 @@ class MessagerUtilsTest extends UtilTest { @Override protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + super.beforeTest(invocationContext,extensionContext); this.messager = this.processingEnv.getMessager(); } diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java index 931c71b18..67a87679d 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java @@ -86,6 +86,7 @@ protected void addCompiledClasses(Set> compiledClasses) { @Override protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + super.beforeTest(invocationContext,extensionContext); TypeElement type = getTypeElement(Object.class); List methods = getDeclaredMethods(type); this.objectMethods = methods; diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java index a177dad1f..225fcc3bd 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java @@ -83,6 +83,7 @@ class ResolvableAnnotationValueVisitorTest extends UtilTest { @Override protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + super.beforeTest(invocationContext, extensionContext); this.visitor = new ResolvableAnnotationValueVisitor(); this.visitor1 = new ResolvableAnnotationValueVisitor(true); this.visitor2 = new ResolvableAnnotationValueVisitor(true, true); diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java index 9cad309cf..57ecefce1 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java @@ -18,6 +18,9 @@ package io.microsphere.lang.model.util; import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; +import io.microsphere.test.service.TestServiceImpl; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -25,6 +28,7 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; +import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.List; @@ -43,6 +47,11 @@ */ public abstract class UtilTest extends AbstractAnnotationProcessingTest { + @Override + protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { + this.testTypeElement = getTypeElement(TestServiceImpl.class); + } + protected List getTypeMirrors(Type... types) { return TypeUtils.getTypeMirrors(processingEnv, types); } From 2514e17c397c34f485d5280d37c5a4ab956595f2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 20:55:20 +0800 Subject: [PATCH 035/438] Add initTestClass to initialize test type Introduce initTestClass(Class) to centralize initialization of test class metadata for annotation-processing tests. The new method sets testClass, testClassName, resolves testTypeElement via Elements, assigns testTypeMirror and casts it to testDeclaredType (DeclaredType). This consolidates repeated setup logic and prepares type information for subsequent processing. --- .../processing/AbstractAnnotationProcessingTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java index 0979eab68..57f4a8daf 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AbstractAnnotationProcessingTest.java @@ -132,6 +132,14 @@ public abstract class AbstractAnnotationProcessingTest { protected void addCompiledClasses(Set> compiledClasses) { } + protected void initTestClass(Class testClass) { + this.testClass = testClass; + this.testClassName = testClass.getName(); + this.testTypeElement = this.elements.getTypeElement(this.testClassName); + this.testTypeMirror = this.testTypeElement.asType(); + this.testDeclaredType = (DeclaredType) this.testTypeMirror; + } + /** * Before Test * From 914e36a6b0079cfebb5f71a18d4fb1aecf8d58dd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 20:55:30 +0800 Subject: [PATCH 036/438] Use initTestClass to initialize test class Replace manual resolution of TypeElement/TypeMirror and multiple direct field assignments with a single call to abstractAnnotationProcessingTest.initTestClass(testClass). Remove now-unused imports (DeclaredType, TypeMirror) and simplify the processor's initialization logic to centralize test class setup. --- .../processing/AnnotationProcessingTestProcessor.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java index abc23576e..7162d5e69 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java @@ -26,8 +26,6 @@ import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import java.lang.reflect.Method; @@ -87,19 +85,12 @@ void prepare(RoundEnvironment roundEnv) { Elements elements = processingEnv.getElementUtils(); Types types = processingEnv.getTypeUtils(); Class testClass = this.invocationContext.getTargetClass(); - String testClassName = testClass.getName(); - TypeElement testTypeElement = elements.getTypeElement(testClassName); - TypeMirror testType = testTypeElement.asType(); abstractAnnotationProcessingTest.roundEnv = roundEnv; abstractAnnotationProcessingTest.processingEnv = processingEnv; abstractAnnotationProcessingTest.elements = elements; abstractAnnotationProcessingTest.types = types; - abstractAnnotationProcessingTest.testClass = testClass; - abstractAnnotationProcessingTest.testClassName = testClassName; - abstractAnnotationProcessingTest.testTypeElement = testTypeElement; - abstractAnnotationProcessingTest.testTypeMirror = testType; - abstractAnnotationProcessingTest.testDeclaredType = (DeclaredType) testType; + abstractAnnotationProcessingTest.initTestClass(testClass); } @Override From 344794c01f5338e8a0bac351f1b9f2c770678ab6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 20:55:48 +0800 Subject: [PATCH 037/438] Use initTestClass in UtilTest.beforeTest Replace direct assignment of testTypeElement with a call to initTestClass(TestServiceImpl.class) in beforeTest. This centralizes test setup and ensures any additional initialization performed by initTestClass is applied for the test class. --- .../src/test/java/io/microsphere/lang/model/util/UtilTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java index 57ecefce1..2730dab41 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/UtilTest.java @@ -49,7 +49,7 @@ public abstract class UtilTest extends AbstractAnnotationProcessingTest { @Override protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { - this.testTypeElement = getTypeElement(TestServiceImpl.class); + initTestClass(TestServiceImpl.class); } protected List getTypeMirrors(Type... types) { From d2dca1afb94cffde88d526e36866d0f923295f57 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 20:55:55 +0800 Subject: [PATCH 038/438] Qualify test fields with 'this.' Update ResolvableAnnotationValueVisitorTest to explicitly reference instance fields by prefixing with 'this.' (visitor, visitor1, visitor2, testTypeElement) to avoid ambiguity and improve clarity. Also fix a duplicated assertion in testVisitString to use visitor2 for the third check. --- .../ResolvableAnnotationValueVisitorTest.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java index 225fcc3bd..3189ce3f9 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ResolvableAnnotationValueVisitorTest.java @@ -87,12 +87,12 @@ protected void beforeTest(ReflectiveInvocationContext invocationContext, this.visitor = new ResolvableAnnotationValueVisitor(); this.visitor1 = new ResolvableAnnotationValueVisitor(true); this.visitor2 = new ResolvableAnnotationValueVisitor(true, true); - this.testAnnotationAttributes = getElementValues(testTypeElement, TestAnnotation.class); + this.testAnnotationAttributes = getElementValues(this.testTypeElement, TestAnnotation.class); } @Test void testVisitBoolean() { - assertEquals(BOOLEAN_VALUE, visitor.visitBoolean(BOOLEAN_VALUE, null)); + assertEquals(BOOLEAN_VALUE, this.visitor.visitBoolean(BOOLEAN_VALUE, null)); assertVisit(this.visitor, "z", BOOLEAN_VALUE); assertVisit(this.visitor1, "z", BOOLEAN_VALUE); assertVisit(this.visitor2, "z", BOOLEAN_VALUE); @@ -100,7 +100,7 @@ void testVisitBoolean() { @Test void testVisitByte() { - assertEquals(BYTE_VALUE, visitor.visitByte(BYTE_VALUE, null)); + assertEquals(BYTE_VALUE, this.visitor.visitByte(BYTE_VALUE, null)); assertVisit(this.visitor, "b", BYTE_VALUE); assertVisit(this.visitor1, "b", BYTE_VALUE); assertVisit(this.visitor2, "b", BYTE_VALUE); @@ -108,7 +108,7 @@ void testVisitByte() { @Test void testVisitChar() { - assertEquals(CHAR_VALUE, visitor.visitChar(CHAR_VALUE, null)); + assertEquals(CHAR_VALUE, this.visitor.visitChar(CHAR_VALUE, null)); assertVisit(this.visitor, "c", CHAR_VALUE); assertVisit(this.visitor1, "c", CHAR_VALUE); assertVisit(this.visitor2, "c", CHAR_VALUE); @@ -116,7 +116,7 @@ void testVisitChar() { @Test void testVisitDouble() { - assertEquals(DOUBLE_VALUE, visitor.visitDouble(DOUBLE_VALUE, null)); + assertEquals(DOUBLE_VALUE, this.visitor.visitDouble(DOUBLE_VALUE, null)); assertVisit(this.visitor, "d", DOUBLE_VALUE); assertVisit(this.visitor1, "d", DOUBLE_VALUE); assertVisit(this.visitor2, "d", DOUBLE_VALUE); @@ -124,7 +124,7 @@ void testVisitDouble() { @Test void testVisitFloat() { - assertEquals(FLOAT_VALUE, visitor.visitFloat(FLOAT_VALUE, null)); + assertEquals(FLOAT_VALUE, this.visitor.visitFloat(FLOAT_VALUE, null)); assertVisit(this.visitor, "f", FLOAT_VALUE); assertVisit(this.visitor1, "f", FLOAT_VALUE); assertVisit(this.visitor2, "f", FLOAT_VALUE); @@ -132,7 +132,7 @@ void testVisitFloat() { @Test void testVisitInt() { - assertEquals(INT_VALUE, visitor.visitInt(INT_VALUE, null)); + assertEquals(INT_VALUE, this.visitor.visitInt(INT_VALUE, null)); assertVisit(this.visitor, "i", INT_VALUE); assertVisit(this.visitor1, "i", INT_VALUE); assertVisit(this.visitor2, "i", INT_VALUE); @@ -140,7 +140,7 @@ void testVisitInt() { @Test void testVisitLong() { - assertEquals(LONG_VALUE, visitor.visitLong(LONG_VALUE, null)); + assertEquals(LONG_VALUE, this.visitor.visitLong(LONG_VALUE, null)); assertVisit(this.visitor, "l", LONG_VALUE); assertVisit(this.visitor1, "l", LONG_VALUE); assertVisit(this.visitor2, "l", LONG_VALUE); @@ -148,7 +148,7 @@ void testVisitLong() { @Test void testVisitShort() { - assertEquals(SHORT_VALUE, visitor.visitShort(SHORT_VALUE, null)); + assertEquals(SHORT_VALUE, this.visitor.visitShort(SHORT_VALUE, null)); assertVisit(this.visitor, "s", SHORT_VALUE); assertVisit(this.visitor1, "s", SHORT_VALUE); assertVisit(this.visitor2, "s", SHORT_VALUE); @@ -156,15 +156,15 @@ void testVisitShort() { @Test void testVisitString() { - assertEquals(STRING_VALUE, visitor.visitString(STRING_VALUE, null)); + assertEquals(STRING_VALUE, this.visitor.visitString(STRING_VALUE, null)); assertVisit(this.visitor, "string", STRING_VALUE); assertVisit(this.visitor1, "string", STRING_VALUE); - assertVisit(this.visitor1, "string", STRING_VALUE); + assertVisit(this.visitor2, "string", STRING_VALUE); } @Test void testVisitType() { - assertEquals(TYPE_VALUE, visitor.visitType(getTypeMirror(TYPE_VALUE), null)); + assertEquals(TYPE_VALUE, this.visitor.visitType(getTypeMirror(TYPE_VALUE), null)); assertVisit(this.visitor, "type", TYPE_VALUE); assertVisit(this.visitor1, "type", TYPE_VALUE.getName()); assertVisit(this.visitor2, "type", TYPE_VALUE.getName()); @@ -204,7 +204,7 @@ void testVisitUnknown() { for (Entry elementValue : this.testAnnotationAttributes.entrySet()) { ExecutableElement attributeMethod = elementValue.getKey(); AnnotationValue annotationValue = elementValue.getValue(); - assertSame(annotationValue, visitor.visitUnknown(annotationValue, attributeMethod)); + assertSame(annotationValue, this.visitor.visitUnknown(annotationValue, attributeMethod)); } } From 1532f508d011b67e7aa0c83da709b5f146f09ae6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 21:16:25 +0800 Subject: [PATCH 039/438] Refactor JSONElementVisitor unsupported tests Rename and reorganize tests in JSONElementVisitorTest to match the visited element types and assertions. Test method names were corrected (package, variable, executable, type, type-parameter) and assertions updated to call the appropriate visitor methods and element getters; a missing test for visitTypeParameter when unsupported was added. Minor formatting cleanup included. --- .../model/util/JSONElementVisitorTest.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java index c18e83147..ade0ce967 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java @@ -145,9 +145,9 @@ void testVisitPackage() { } @Test - void testVisitVariableOnUnsupported() { + void testVisitPackageOnUnsupported() { supported = false; - assertFalse(visitor.visitVariable(null, jsonBuilder)); + assertFalse(visitor.visitPackage(this.elements.getPackageElement("io.microsphere.annotation.processor.model.util"), jsonBuilder)); } @Test @@ -155,7 +155,6 @@ void testVisitVariableAsEnumConstant() { VariableElement element = getField(Color.class, "RED"); assertTrue(visitor.visitVariable(element, jsonBuilder)); assertJson("visitVariableAsEnumConstant"); - } @Test @@ -175,9 +174,9 @@ void testVisitVariableAsParameter() { } @Test - void testVisitExecutableOnUnsupported() { + void testVisitVariableOnUnsupported() { supported = false; - assertFalse(visitor.visitExecutable(null, jsonBuilder)); + assertFalse(visitor.visitVariable(null, jsonBuilder)); } @Test @@ -195,10 +194,9 @@ void testVisitExecutableAsMethod() { } @Test - void testVisitTypeOnUnsupported() { + void testVisitExecutableOnUnsupported() { supported = false; - TypeElement typeElement = getTypeElement(Serializable.class); - assertFalse(visitor.visitType(typeElement, jsonBuilder)); + assertFalse(visitor.visitExecutable(null, jsonBuilder)); } @Test @@ -230,9 +228,10 @@ void testVisitTypeAsAnnotationType() { } @Test - void testVisitTypeParameterOnUnsupported() { + void testVisitTypeOnUnsupported() { supported = false; - assertFalse(visitor.visitTypeParameter(null, jsonBuilder)); + TypeElement typeElement = getTypeElement(Serializable.class); + assertFalse(visitor.visitType(typeElement, jsonBuilder)); } @Test @@ -247,6 +246,12 @@ void testVisitTypeParameter() { } } + @Test + void testVisitTypeParameterOnUnsupported() { + supported = false; + assertFalse(visitor.visitTypeParameter(null, jsonBuilder)); + } + void assertJson(String expected) { assertEquals(expected, jsonBuilder.toString()); } From ef24c72e056e58c53e1b3b4dabbf28f214703ab5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 21:43:00 +0800 Subject: [PATCH 040/438] Return early when type not supported If supportsType(e) is false, visitType now returns immediately to avoid processing unsupported types. Also initialize 'appended' from super.visitType(e, jsonBuilder) and keep existing member-visit logic, simplifying control flow and making behavior more explicit. --- .../lang/model/util/JSONElementVisitor.java | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java index 36a20126c..46cf974e0 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java @@ -77,11 +77,12 @@ public final Boolean visitExecutable(ExecutableElement e, StringBuilder jsonBuil @Override public final Boolean visitType(TypeElement e, StringBuilder jsonBuilder) { - boolean appended = false; - if (supportsType(e) && super.visitType(e, jsonBuilder)) { - appended = true; + if (!supportsType(e)) { + return false; } + boolean appended = super.visitType(e, jsonBuilder); + // The declared members of the type element if (visitMembers(e.getEnclosedElements(), jsonBuilder)) { appended = true; @@ -92,21 +93,7 @@ public final Boolean visitType(TypeElement e, StringBuilder jsonBuilder) { @Override public final Boolean visitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { - if (!supports(e)) { - return FALSE; - } - - boolean appended = false; - if (supportsTypeParameter(e) && doVisitTypeParameter(e, jsonBuilder)) { - appended = true; - } - - // The declared members of the type element - if (visitMembers(e.getEnclosedElements(), jsonBuilder)) { - appended = true; - } - - return appended; + return supportsTypeParameter(e) && doVisitTypeParameter(e, jsonBuilder); } protected boolean visitMembers(List members, StringBuilder jsonBuilder) { From 3442b9f1fc95bf05b0801d0201b1705cd703c710 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 22:05:42 +0800 Subject: [PATCH 041/438] Use ListUtils.first for annotation retrieval Replace manual .get(0) checks with ListUtils.first by adding a static import and using first(annotations) in AnnotationUtils. Simplifies code for obtaining the first annotation and centralizes handling of empty lists. --- .../java/io/microsphere/lang/model/util/AnnotationUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java index 53462b44f..5d33af6fb 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java @@ -44,6 +44,7 @@ import static io.microsphere.collection.CollectionUtils.isEmpty; import static io.microsphere.collection.CollectionUtils.size; +import static io.microsphere.collection.ListUtils.first; import static io.microsphere.collection.MapUtils.immutableEntry; import static io.microsphere.collection.MapUtils.isEmpty; import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; @@ -169,7 +170,7 @@ static AnnotationMirror getAnnotation(AnnotatedConstruct annotatedConstruct, Cha return null; } List annotations = getAnnotations(annotatedConstruct, annotationClassName); - return annotations.isEmpty() ? null : annotations.get(0); + return first(annotations); } /** @@ -606,7 +607,7 @@ static AnnotationMirror findAnnotation(Element element, CharSequence annotationC return null; } List annotations = findAllAnnotations(element, annotation -> matchesAnnotationTypeName(annotation, annotationClassName)); - return isEmpty(annotations) ? null : annotations.get(0); + return first(annotations); } /** From d63ffdfcd12d73f975e490e4336721350a2dbba4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 22:08:23 +0800 Subject: [PATCH 042/438] Use ListUtils.first in MethodUtils Replace manual empty-check and get(0) pattern with a static import of ListUtils.first when returning the first declared method. Adds import io.microsphere.collection.ListUtils.first and uses first(allDeclaredMethods) to keep the previous null-on-empty behavior while simplifying the code. --- .../java/io/microsphere/lang/model/util/MethodUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java index f4e741343..de250a897 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/MethodUtils.java @@ -35,6 +35,7 @@ import java.util.function.Predicate; import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.collection.ListUtils.first; import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; import static io.microsphere.lang.function.Predicates.and; import static io.microsphere.lang.function.Streams.filterFirst; @@ -666,7 +667,7 @@ static ExecutableElement findMethod(TypeMirror type, String methodName, Type... return null; } List allDeclaredMethods = findAllDeclaredMethods(type, method -> matches(method, methodName, parameterTypes)); - return allDeclaredMethods.isEmpty() ? null : allDeclaredMethods.get(0); + return first(allDeclaredMethods); } /** @@ -730,7 +731,7 @@ static ExecutableElement findMethod(TypeMirror type, String methodName, CharSequ return null; } List allDeclaredMethods = findAllDeclaredMethods(type, method -> matches(method, methodName, parameterTypeNames)); - return allDeclaredMethods.isEmpty() ? null : allDeclaredMethods.get(0); + return first(allDeclaredMethods); } /** From 3daabb99bff947d1d62b772ac1bbda2ecc0af0c0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 22:08:37 +0800 Subject: [PATCH 043/438] Remove optional flag from core dependency Deleted the true element from the microsphere-java-core dependency in microsphere-java-test/pom.xml so the core artifact is no longer optional and will be included on the test module's classpath (ensuring tests have access to core). --- microsphere-java-test/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/microsphere-java-test/pom.xml b/microsphere-java-test/pom.xml index 568f1add5..40504c98e 100644 --- a/microsphere-java-test/pom.xml +++ b/microsphere-java-test/pom.xml @@ -25,7 +25,6 @@ io.github.microsphere-projects microsphere-java-core ${revision} - true From 0714652b0d5a84249e3a43624b892caba9966cb5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Feb 2026 22:16:46 +0800 Subject: [PATCH 044/438] Qualify echoMethod references in tests Update ElementUtilsTest to consistently reference the instance field by qualifying echoMethod with this. Also add additional null-case assertions for matchParameterTypes (NULL_METHOD and NULL_CLASS_ARRAY) and normalize spacing in super.beforeTest. These changes improve clarity and null-safety in the test code. --- .../lang/model/util/ElementUtilsTest.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java index e13704df3..df59a4162 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java @@ -94,20 +94,20 @@ class ElementUtilsTest extends UtilTest { @Override protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { - super.beforeTest(invocationContext,extensionContext); + super.beforeTest(invocationContext, extensionContext); this.echoMethod = findMethod(testTypeElement, "echo", "java.lang.String"); } @Test void testMatchesElementTypeElementKind() { assertTrue(matchesElementKind(echoMethod, METHOD)); - assertFalse(matchesElementKind(echoMethod, FIELD)); + assertFalse(matchesElementKind(this.echoMethod, FIELD)); } @Test void testMatchesElementTypeElementKindOnNull() { assertFalse(matchesElementKind(NULL_ELEMENT, FIELD)); - assertFalse(matchesElementKind(echoMethod, NULL_ELEMENT_KIND)); + assertFalse(matchesElementKind(this.echoMethod, NULL_ELEMENT_KIND)); } @Test @@ -306,33 +306,35 @@ void testFilterElements() { @Test void testFilterElementsOnNull() { assertEmptyList(filterElements(NULL_LIST, alwaysTrue())); - List methods = ofList(echoMethod); + List methods = ofList(this.echoMethod); assertSame(emptyList(), filterElements(methods, NULL_PREDICATE_ARRAY)); } @Test void testFilterElementsOnEmpty() { assertEmptyList(filterElements(emptyList(), alwaysTrue())); - List methods = ofList(echoMethod); + List methods = ofList(this.echoMethod); assertEquals(methods, filterElements(methods)); } @Test void testMatchParameterTypes() { - assertTrue(matchParameterTypes(echoMethod.getParameters(), String.class)); - assertFalse(matchParameterTypes(echoMethod.getParameters(), Object.class)); + assertTrue(matchParameterTypes(this.echoMethod.getParameters(), String.class)); + assertFalse(matchParameterTypes(this.echoMethod.getParameters(), Object.class)); } @Test void testMatchParameterTypesOnNull() { assertFalse(matchParameterTypes(NULL_LIST, String.class)); assertFalse(matchParameterTypes(emptyList(), NULL_CLASS_ARRAY)); + assertFalse(matchParameterTypes(NULL_METHOD, String.class)); + assertFalse(matchParameterTypes(this.echoMethod, NULL_CLASS_ARRAY)); } @Test void testMatchParameterTypeNames() { - assertTrue(matchParameterTypeNames(echoMethod.getParameters(), "java.lang.String")); - assertFalse(matchParameterTypeNames(echoMethod.getParameters(), "java.lang.Object")); + assertTrue(matchParameterTypeNames(this.echoMethod.getParameters(), "java.lang.String")); + assertFalse(matchParameterTypeNames(this.echoMethod.getParameters(), "java.lang.Object")); } @Test From ae54893fcfdbbcd4fb7b6697c24ec975968401c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 01:34:19 +0000 Subject: [PATCH 045/438] Bump org.springframework:spring-framework-bom from 7.0.2 to 7.0.3 Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 7.0.2 to 7.0.3. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v7.0.2...v7.0.3) --- updated-dependencies: - dependency-name: org.springframework:spring-framework-bom dependency-version: 7.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 6172f5df4..23460ca6c 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -22,7 +22,7 @@ 1.3.2 3.0.2 1.3.2 - 7.0.2 + 7.0.3 2.0.17 1.5.24 From 1424da904a4c577670ec219080c8b06dd34a4bfe Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 13:52:59 +0800 Subject: [PATCH 046/438] Test loadClass returns null for missing class Add a null-case assertion to ClassUtilsTest: import assertNull and assert that loadClass("not-found-class") returns null. This verifies that loadClass handles unknown class names gracefully. --- .../java/io/microsphere/lang/model/util/ClassUtilsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ClassUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ClassUtilsTest.java index 5f6708723..c6857d5ed 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ClassUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ClassUtilsTest.java @@ -23,6 +23,7 @@ import static io.microsphere.lang.model.util.ClassUtils.getClassName; import static io.microsphere.lang.model.util.ClassUtils.loadClass; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; /** @@ -47,5 +48,6 @@ void testLoadClassOnTypeMirror() { @Test void testLoadClass() { assertSame(ComponentScan.Filter.class, loadClass("org.springframework.context.annotation.ComponentScan.Filter")); + assertNull(loadClass("not-found-class")); } } From 632bc1bda1c172b8ac5a3a256c2505b0b91f1e93 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 14:13:25 +0800 Subject: [PATCH 047/438] Re-throw test failure root cause as Error After invoking afterTest, if a test produced a failure, extract its root cause and rethrow it wrapped as an Error to preserve the original cause and halt processing. Adds static imports for ExceptionUtils.wrap and ThrowableUtils.getRootCause. --- .../processing/AnnotationProcessingTestProcessor.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java index 7162d5e69..3192d0140 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/AnnotationProcessingTestProcessor.java @@ -32,6 +32,8 @@ import java.util.Set; import static io.microsphere.constants.SymbolConstants.WILDCARD; +import static io.microsphere.util.ExceptionUtils.wrap; +import static io.microsphere.util.ThrowableUtils.getRootCause; import static javax.lang.model.SourceVersion.latestSupported; /** @@ -76,6 +78,10 @@ public boolean process(Set annotations, RoundEnvironment } finally { abstractAnnotationProcessingTest.afterTest(invocationContext, extensionContext, result, failure); } + if (failure != null) { + Throwable cause = getRootCause(failure); + throw wrap(cause, Error.class); + } } return false; } From a2d6b6a8d23c4bcadce285cd19a3cf5fc5f05607 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 14:15:08 +0800 Subject: [PATCH 048/438] Improve matchesElementType test assertions Replace a bare matchesElementType call with explicit assertions in ElementUtilsTest: assertTrue for the expected TYPE match and assertFalse for a non-matching PACKAGE. This makes the test verify both positive and negative outcomes rather than relying on a no-op call. --- .../java/io/microsphere/lang/model/util/ElementUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java index df59a4162..46c2e03cb 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java @@ -281,7 +281,8 @@ void testMatchesElementTypeWithArray() { @Test void testMatchesElementTypeWithElement() { - matchesElementType(this.testTypeElement, TYPE); + assertTrue(matchesElementType(this.testTypeElement, TYPE)); + assertFalse(matchesElementType(this.testTypeElement, PACKAGE)); } @Test From 31c7f9fae0968312b5d304556093cc11bd8fff64 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 15:04:24 +0800 Subject: [PATCH 049/438] Make context and environment protected Change ApplicationContext and Environment field visibility from package-private to protected so subclasses can access them directly. @Autowired on the ApplicationContext is retained; Environment remains unannotated. --- .../java/io/microsphere/test/service/TestServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/service/TestServiceImpl.java b/microsphere-java-test/src/main/java/io/microsphere/test/service/TestServiceImpl.java index a97658884..27a53b2d3 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/service/TestServiceImpl.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/service/TestServiceImpl.java @@ -77,9 +77,9 @@ public class TestServiceImpl extends GenericTestService implements TestService, AutoCloseable, Serializable { @Autowired - ApplicationContext context; + protected ApplicationContext context; - Environment environment; + protected Environment environment; public TestServiceImpl() { this(null); From 1be84f729fb408d9806613372764ec0216a3ff58 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 15:04:36 +0800 Subject: [PATCH 050/438] Use PROTECTED modifier in ElementUtilsTest Replace PRIVATE with PROTECTED in the import and the assertion so the test verifies the protected modifier on the field. This aligns the test expectation with the field's actual visibility. --- .../java/io/microsphere/lang/model/util/ElementUtilsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java index 46c2e03cb..b65ea7b2c 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/ElementUtilsTest.java @@ -72,7 +72,7 @@ import static javax.lang.model.element.ElementKind.PARAMETER; import static javax.lang.model.element.ElementKind.RESOURCE_VARIABLE; import static javax.lang.model.element.ElementKind.STATIC_INIT; -import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.element.Modifier.PROTECTED; import static javax.lang.model.util.ElementFilter.fieldsIn; import static javax.lang.model.util.ElementFilter.methodsIn; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -127,7 +127,7 @@ void testIsPublicNonStaticOnNull() { void testHasModifiers() { List members = getAllDeclaredMembers(testTypeElement.asType()); List fields = fieldsIn(members); - assertTrue(hasModifiers(fields.get(0), PRIVATE)); + assertTrue(hasModifiers(fields.get(0), PROTECTED)); } @Test From 3d65ddf6ec6fc0751ace0a74ba6382fdfc2229b8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 15:04:49 +0800 Subject: [PATCH 051/438] Fix expected type in JSONAnnotationValueVisitorTest Update the expected JSON fragment in testVisit from "io.microsphere.annotation.processor.GenericTestService" to "io.microsphere.test.service.GenericTestService" to match the class/package change. This keeps the test assertion in microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java in sync with the refactored/relocated test service class. --- .../lang/model/util/JSONAnnotationValueVisitorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java index cf1e85c0f..d89d3c732 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONAnnotationValueVisitorTest.java @@ -103,7 +103,7 @@ void testVisitString() { @Test void testVisitType() { - testVisit("type", "\"type\":\"io.microsphere.annotation.processor.GenericTestService\""); + testVisit("type", "\"type\":\"io.microsphere.test.service.GenericTestService\""); } @Test From 24cbc8852ef5f0e8928dc3bf0e5ea0a46487fcec Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 15:05:08 +0800 Subject: [PATCH 052/438] Update package refs in JSONElementVisitorTest Replace outdated package references in JSONElementVisitorTest: change io.microsphere.annotation.processor.model.util to io.microsphere.lang.model.util in testVisitPackage and testVisitPackageOnUnsupported. Keeps tests aligned with the current package structure after refactor. --- .../microsphere/lang/model/util/JSONElementVisitorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java index ade0ce967..f9c20e905 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/JSONElementVisitorTest.java @@ -140,14 +140,14 @@ public Boolean visitTypeAsAnnotationType(TypeElement e, StringBuilder stringBuil @Test void testVisitPackage() { - assertTrue(visitor.visitPackage(this.elements.getPackageElement("io.microsphere.annotation.processor.model.util"), jsonBuilder)); + assertTrue(visitor.visitPackage(this.elements.getPackageElement("io.microsphere.lang.model.util"), jsonBuilder)); assertJson("visitPackage"); } @Test void testVisitPackageOnUnsupported() { supported = false; - assertFalse(visitor.visitPackage(this.elements.getPackageElement("io.microsphere.annotation.processor.model.util"), jsonBuilder)); + assertFalse(visitor.visitPackage(this.elements.getPackageElement("io.microsphere.lang.model.util"), jsonBuilder)); } @Test From f16d29471e0f0c231a01e581634f35023faf1766 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 15:13:20 +0800 Subject: [PATCH 053/438] Ignore testOnFailure exceptions via extension Make AnnotationProcessingTest a JUnit 5 exception handler and use it as an extension for testOnFailure. The class now implements TestExecutionExceptionHandler and is applied to the failing test with @ExtendWith(AnnotationProcessingTest.class). handleTestExecutionException suppresses the RuntimeException thrown by testOnFailure so the test can exercise failure behavior without failing the whole test run. Added the required JUnit imports. --- .../processing/AnnotationProcessingTest.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java index 287fb77f9..568cf0d3d 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java @@ -18,8 +18,10 @@ package io.microsphere.test.annotation.processing; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ReflectiveInvocationContext; +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; import javax.lang.model.element.Element; import javax.lang.model.type.TypeMirror; @@ -38,7 +40,7 @@ * @see AbstractAnnotationProcessingTest * @since 1.0.0 */ -public class AnnotationProcessingTest extends AbstractAnnotationProcessingTest { +public class AnnotationProcessingTest extends AbstractAnnotationProcessingTest implements TestExecutionExceptionHandler { @Test void test() { @@ -72,6 +74,7 @@ void test() { } @Test + @ExtendWith(AnnotationProcessingTest.class) void testOnFailure() { throw new RuntimeException("For testing"); } @@ -83,4 +86,14 @@ protected void afterTest(ReflectiveInvocationContext invocationContext, assertEquals("For testing", failure.getMessage()); } } + + @Override + public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { + if (throwable != null) { + Method method = context.getTestMethod().get(); + if ("testOnFailure".equals(method.getName())) { + // ingnore + } + } + } } From 418d96ec02b96d58683a535e7968fc1d28207930 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 15:20:42 +0800 Subject: [PATCH 054/438] Remove final newline from AnnotationProcessingTest Remove the trailing newline at end-of-file for microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java. No other code changes were made. --- .../test/annotation/processing/AnnotationProcessingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java index 568cf0d3d..9b40f9b77 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java @@ -96,4 +96,4 @@ public void handleTestExecutionException(ExtensionContext context, Throwable thr } } } -} +} \ No newline at end of file From 380521c3987d6808ce317842bb4caef13ea2ef33 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 15:53:39 +0800 Subject: [PATCH 055/438] Enable JUnit Jupiter extensions & parallelism Add junit-platform.properties to enable JUnit Jupiter extension autodetection and parallel test execution. Sets junit.jupiter.extensions.autodetection.enabled=true and configures parallel execution mode to concurrent for both default and classes, improving test discovery and runtime performance. File added at microsphere-java-test/src/main/resources. --- .../src/main/resources/junit-platform.properties | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 microsphere-java-test/src/main/resources/junit-platform.properties diff --git a/microsphere-java-test/src/main/resources/junit-platform.properties b/microsphere-java-test/src/main/resources/junit-platform.properties new file mode 100644 index 000000000..1e4594242 --- /dev/null +++ b/microsphere-java-test/src/main/resources/junit-platform.properties @@ -0,0 +1,5 @@ +junit.jupiter.extensions.autodetection.enabled = true + +junit.jupiter.execution.parallel.enabled = true +junit.jupiter.execution.parallel.mode.default = concurrent +junit.jupiter.execution.parallel.mode.classes.default = concurrent \ No newline at end of file From 8b003226ea0eca03ad8fe76e2d3f40715f05fb33 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 18:20:33 +0800 Subject: [PATCH 056/438] Refactor getElementValue and update tests Simplify AnnotationUtils.getElementValue: narrow attribute method search by combining the missing-value and name match into the predicate, use first(...) to pick the attribute method, and return null early if no value is found. Update unit tests to reference super.testTypeElement/testTypeMirror consistently, add missing imports (PAYLOAD, assertNotNull), and introduce new tests for default-attribute-null and element-not-found cases. Also strengthen assertAttributeEntry (null checks and clearer expected-value comparison) and adjust related helper methods to match the new behaviors. --- .../lang/model/util/AnnotationUtils.java | 17 +- .../lang/model/util/AnnotationUtilsTest.java | 224 ++++++++++-------- 2 files changed, 139 insertions(+), 102 deletions(-) diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java index 5d33af6fb..816854717 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java @@ -1582,17 +1582,18 @@ static Entry getElementValue(AnnotationMirro if (withDefault && annotationValue == null) { // not found if the default value is required DeclaredType annotationType = annotation.getAnnotationType(); - List attributeMethods = findDeclaredMethods(annotationType, method -> !elementValues.containsKey(method)); - int size = attributeMethods.size(); - for (int i = 0; i < size; i++) { - attributeMethod = attributeMethods.get(i); - if (matchesAttributeMethod(attributeMethod, attributeName)) { - annotationValue = attributeMethod.getDefaultValue(); - break; - } + List attributeMethods = findDeclaredMethods(annotationType, method -> + !elementValues.containsKey(method) && matchesAttributeMethod(method, attributeName)); + attributeMethod = first(attributeMethods); + if (attributeMethod != null) { + annotationValue = attributeMethod.getDefaultValue(); } } + if (annotationValue == null) { + return null; + } + return immutableEntry(attributeMethod, annotationValue); } diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java index 4dec272bb..bb9a48f9b 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java @@ -82,9 +82,11 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.util.Collections.emptyMap; +import static javax.xml.ws.Service.Mode.PAYLOAD; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -109,21 +111,21 @@ void testGetAnnotationWithClassName() { @Test void testGetAnnotationOnNull() { - assertNull(getAnnotation(testTypeElement, NULL_CLASS)); - assertNull(getAnnotation(testTypeElement.asType(), NULL_CLASS)); + assertNull(getAnnotation(super.testTypeElement, NULL_CLASS)); + assertNull(getAnnotation(super.testTypeElement.asType(), NULL_CLASS)); assertNull(getAnnotation(NULL_ANNOTATED_CONSTRUCT, NULL_CLASS)); } @Test void testGetAnnotationWithClassNameOnNull() { - assertNull(getAnnotation(testTypeElement, NULL_STRING)); - assertNull(getAnnotation(testTypeElement.asType(), NULL_STRING)); + assertNull(getAnnotation(super.testTypeElement, NULL_STRING)); + assertNull(getAnnotation(super.testTypeElement.asType(), NULL_STRING)); assertNull(getAnnotation(NULL_ANNOTATED_CONSTRUCT, NULL_STRING)); } @Test void testGetAnnotations() { - List annotations = getAnnotations(testTypeElement); + List annotations = getAnnotations(super.testTypeElement); assertEquals(4, annotations.size()); assertAnnotation(annotations.get(0), Service.class); assertAnnotation(annotations.get(1), ServiceMode.class); @@ -146,13 +148,13 @@ void testGetAnnotationsWithAnnotationClass() { @Test void testGetAnnotationsWithAnnotationClassOnNull() { assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, NULL_CLASS).isEmpty()); - assertTrue(getAnnotations(testTypeElement, NULL_CLASS).isEmpty()); + assertTrue(getAnnotations(super.testTypeElement, NULL_CLASS).isEmpty()); assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, Service.class).isEmpty()); } @Test void testGetAnnotationsWithAnnotationClassOnNotFound() { - List annotations = getAnnotations(testTypeElement, Override.class); + List annotations = getAnnotations(super.testTypeElement, Override.class); assertEquals(0, annotations.size()); } @@ -165,16 +167,16 @@ void testGetAnnotationsWithAnnotationClassName() { @Test void testGetAnnotationsWithAnnotationClassNameOnNull() { assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, NULL_STRING).isEmpty()); - assertTrue(getAnnotations(testTypeElement, NULL_STRING).isEmpty()); + assertTrue(getAnnotations(super.testTypeElement, NULL_STRING).isEmpty()); assertTrue(getAnnotations(NULL_ANNOTATED_CONSTRUCT, "org.springframework.stereotype.Service").isEmpty()); } @Test void testGetAllAnnotations() { - List annotations = getAllAnnotations(testTypeElement); + List annotations = getAllAnnotations(super.testTypeElement); assertEquals(5, annotations.size()); - annotations = getAllAnnotations(testTypeMirror); + annotations = getAllAnnotations(super.testTypeMirror); assertEquals(5, annotations.size()); } @@ -186,16 +188,16 @@ void testGetAllAnnotationsOnNull() { @Test void testGetAllAnnotationsWithAnnotationClass() { - List annotations = getAllAnnotations(testTypeElement, Override.class); + List annotations = getAllAnnotations(super.testTypeElement, Override.class); assertEquals(0, annotations.size()); - annotations = getAllAnnotations(testTypeMirror, Override.class); + annotations = getAllAnnotations(super.testTypeMirror, Override.class); assertEquals(0, annotations.size()); - annotations = getAllAnnotations(testTypeElement, Service.class); + annotations = getAllAnnotations(super.testTypeElement, Service.class); assertEquals(1, annotations.size()); - annotations = getAllAnnotations(testTypeMirror, Service.class); + annotations = getAllAnnotations(super.testTypeMirror, Service.class); assertEquals(1, annotations.size()); annotations = getAllAnnotations(processingEnv, TestServiceImpl.class); @@ -212,17 +214,17 @@ void testGetAllAnnotationsWithAnnotationClassOnNull() { assertEmptyList(getAllAnnotations(NULL_TYPE_MIRROR, Service.class)); assertEmptyList(getAllAnnotations(NULL_PROCESSING_ENVIRONMENT, Service.class)); - assertEmptyList(getAllAnnotations(testTypeElement, NULL_CLASS)); - assertEmptyList(getAllAnnotations(testTypeMirror, NULL_CLASS)); + assertEmptyList(getAllAnnotations(super.testTypeElement, NULL_CLASS)); + assertEmptyList(getAllAnnotations(super.testTypeMirror, NULL_CLASS)); assertEmptyList(getAllAnnotations(processingEnv, NULL_CLASS)); } @Test void testGetAllAnnotationsWithAnnotationClassName() { - List annotations = getAllAnnotations(testTypeElement, "java.lang.Override"); + List annotations = getAllAnnotations(super.testTypeElement, "java.lang.Override"); assertEquals(0, annotations.size()); - annotations = getAllAnnotations(testTypeMirror, "org.springframework.stereotype.Service"); + annotations = getAllAnnotations(super.testTypeMirror, "org.springframework.stereotype.Service"); assertEquals(1, annotations.size()); } @@ -234,8 +236,8 @@ void testGetAllAnnotationsWithAnnotationClassNameOnNull() { assertTrue(getAllAnnotations(NULL_ELEMENT, "org.springframework.stereotype.Service").isEmpty()); assertTrue(getAllAnnotations(NULL_TYPE_MIRROR, "org.springframework.stereotype.Service").isEmpty()); - assertEmptyList(getAllAnnotations(testTypeElement, NULL_STRING)); - assertEmptyList(getAllAnnotations(testTypeMirror, NULL_STRING)); + assertEmptyList(getAllAnnotations(super.testTypeElement, NULL_STRING)); + assertEmptyList(getAllAnnotations(super.testTypeMirror, NULL_STRING)); } @Test @@ -246,23 +248,23 @@ void testFindAnnotation() { @Test void testFindAnnotationOnNotFound() { - assertNull(findAnnotation(testTypeMirror, Target.class)); - assertNull(findAnnotation(testTypeElement, Target.class)); - assertNull(findAnnotation(testTypeMirror, Override.class)); - assertNull(findAnnotation(testTypeElement, Override.class)); + assertNull(findAnnotation(super.testTypeMirror, Target.class)); + assertNull(findAnnotation(super.testTypeElement, Target.class)); + assertNull(findAnnotation(super.testTypeMirror, Override.class)); + assertNull(findAnnotation(super.testTypeElement, Override.class)); } @Test void testFindAnnotationOnNull() { assertNull(findAnnotation(NULL_ELEMENT, NULL_CLASS)); assertNull(findAnnotation(NULL_TYPE_MIRROR, NULL_CLASS)); - assertNull(findAnnotation(testTypeMirror, NULL_CLASS)); - assertNull(findAnnotation(testTypeElement, NULL_CLASS)); + assertNull(findAnnotation(super.testTypeMirror, NULL_CLASS)); + assertNull(findAnnotation(super.testTypeElement, NULL_CLASS)); assertNull(findAnnotation(NULL_ELEMENT, NULL_STRING)); assertNull(findAnnotation(NULL_TYPE_MIRROR, NULL_STRING)); - assertNull(findAnnotation(testTypeMirror, NULL_STRING)); - assertNull(findAnnotation(testTypeElement, NULL_STRING)); + assertNull(findAnnotation(super.testTypeMirror, NULL_STRING)); + assertNull(findAnnotation(super.testTypeElement, NULL_STRING)); } @Test @@ -274,19 +276,19 @@ void testFindMetaAnnotationWithAnnotationClass() { @Test void testFindMetaAnnotationWithAnnotationClassOnNotFound() { - assertNull(findMetaAnnotation(testTypeElement, Service.class)); + assertNull(findMetaAnnotation(super.testTypeElement, Service.class)); } @Test void testFindMetaAnnotationWithAnnotationClassNameOnNotFound() { - assertNull(findMetaAnnotation(testTypeElement, "org.springframework.stereotype.Service")); + assertNull(findMetaAnnotation(super.testTypeElement, "org.springframework.stereotype.Service")); } @Test void testFindMetaAnnotationWithAnnotationClassOnNull() { assertNull(findMetaAnnotation(NULL_ELEMENT, NULL_CLASS)); assertNull(findMetaAnnotation(NULL_ELEMENT, Service.class)); - assertNull(findMetaAnnotation(testTypeElement, NULL_CLASS)); + assertNull(findMetaAnnotation(super.testTypeElement, NULL_CLASS)); } @Test @@ -300,30 +302,30 @@ void testFindMetaAnnotationWithAnnotationClassName() { void testFindMetaAnnotationWithAnnotationClassNameOnNull() { assertNull(findMetaAnnotation(NULL_ELEMENT, NULL_STRING)); assertNull(findMetaAnnotation(NULL_ELEMENT, "test")); - assertNull(findMetaAnnotation(testTypeElement, NULL_STRING)); + assertNull(findMetaAnnotation(super.testTypeElement, NULL_STRING)); } @Test void testFindAllAnnotationsWithTypeMirror() { - List annotations = findAllAnnotations(testTypeMirror, alwaysTrue()); + List annotations = findAllAnnotations(super.testTypeMirror, alwaysTrue()); assertEquals(5, annotations.size()); - annotations = findAllAnnotations(testTypeMirror, alwaysFalse()); + annotations = findAllAnnotations(super.testTypeMirror, alwaysFalse()); assertEmptyList(annotations); } @Test void testFindAllAnnotationsWithTypeElement() { - List annotations = findAllAnnotations(testTypeElement, alwaysTrue()); + List annotations = findAllAnnotations(super.testTypeElement, alwaysTrue()); assertEquals(5, annotations.size()); - annotations = findAllAnnotations(testTypeElement, alwaysFalse()); + annotations = findAllAnnotations(super.testTypeElement, alwaysFalse()); assertEmptyList(annotations); } @Test void testFindAllAnnotationsWithMethod() { - ExecutableElement method = findMethod(testTypeElement, "echo", String.class); + ExecutableElement method = findMethod(super.testTypeElement, "echo", String.class); List annotations = findAllAnnotations(method, alwaysTrue()); assertEquals(1, annotations.size()); @@ -358,13 +360,13 @@ void testFindAllAnnotationsWithMethodParameters() { @Test void testFindAllAnnotationsWithField() { - VariableElement field = findField(testTypeElement, "context"); + VariableElement field = findField(super.testTypeElement, "context"); List annotations = findAllAnnotations(field, alwaysTrue()); assertEquals(1, annotations.size()); assertAnnotation(annotations.get(0), Autowired.class); - field = findField(testTypeElement, "environment"); + field = findField(super.testTypeElement, "environment"); annotations = findAllAnnotations(field, alwaysTrue()); assertEmptyList(annotations); } @@ -401,38 +403,38 @@ void testFindAllAnnotationsOnNull() { @Test void testMatchesAnnotationClass() { - AnnotationMirror annotation = findAnnotation(testTypeElement, Service.class); + AnnotationMirror annotation = findAnnotation(super.testTypeElement, Service.class); assertTrue(AnnotationUtils.matchesAnnotationType(annotation, Service.class)); } @Test void testMatchesAnnotationClassOnNull() { assertFalse(AnnotationUtils.matchesAnnotationType(NULL_ANNOTATION_MIRROR, Service.class)); - assertFalse(AnnotationUtils.matchesAnnotationType(findAnnotation(testTypeElement, Service.class), NULL_CLASS)); + assertFalse(AnnotationUtils.matchesAnnotationType(findAnnotation(super.testTypeElement, Service.class), NULL_CLASS)); } @Test void testMatchesAnnotationTypeName() { - AnnotationMirror annotation = findAnnotation(testTypeElement, "org.springframework.stereotype.Service"); + AnnotationMirror annotation = findAnnotation(super.testTypeElement, "org.springframework.stereotype.Service"); assertTrue(matchesAnnotationTypeName(annotation, "org.springframework.stereotype.Service")); } @Test void testMatchesAnnotationTypeNameOnNull() { assertFalse(matchesAnnotationTypeName(NULL_ANNOTATION_MIRROR, "org.springframework.stereotype.Service")); - assertFalse(matchesAnnotationTypeName(findAnnotation(testTypeElement, "org.springframework.stereotype.Service"), NULL_STRING)); + assertFalse(matchesAnnotationTypeName(findAnnotation(super.testTypeElement, "org.springframework.stereotype.Service"), NULL_STRING)); } @Test void testGetAttribute() { - assertEquals("testService", getAttribute(findAnnotation(testTypeElement, Service.class), "value")); - assertEquals("testService", getAttribute(findAnnotation(testTypeElement, Service.class), "value", false)); - assertEquals("/echo", getAttribute(findAnnotation(testTypeElement, Path.class), "value")); + assertEquals("testService", getAttribute(findAnnotation(super.testTypeElement, Service.class), "value")); + assertEquals("testService", getAttribute(findAnnotation(super.testTypeElement, Service.class), "value", false)); + assertEquals("/echo", getAttribute(findAnnotation(super.testTypeElement, Path.class), "value")); - assertNull(getAttribute(findAnnotation(testTypeElement, Path.class), NULL_STRING)); - assertNull(getAttribute(findAnnotation(testTypeElement, NULL_CLASS), NULL_STRING)); + assertNull(getAttribute(findAnnotation(super.testTypeElement, Path.class), NULL_STRING)); + assertNull(getAttribute(findAnnotation(super.testTypeElement, NULL_CLASS), NULL_STRING)); - ExecutableElement echoMethod = findMethod(testTypeElement, "echo", String.class); + ExecutableElement echoMethod = findMethod(super.testTypeElement, "echo", String.class); AnnotationMirror cacheableAnnotation = findAnnotation(echoMethod, Cacheable.class); String[] cacheNames = getAttribute(cacheableAnnotation, "cacheNames"); assertArrayEquals(ofArray("cache-1", "cache-2"), cacheNames); @@ -455,54 +457,54 @@ void testGetValue() { @Test void testIsAnnotationPresentOnAnnotationClass() { - assertTrue(isAnnotationPresent(testTypeElement, Service.class)); - assertTrue(isAnnotationPresent(testTypeElement, Component.class)); - assertTrue(isAnnotationPresent(testTypeElement, ServiceMode.class)); - assertTrue(isAnnotationPresent(testTypeElement, Inherited.class)); - assertTrue(isAnnotationPresent(testTypeElement, Documented.class)); + assertTrue(isAnnotationPresent(super.testTypeElement, Service.class)); + assertTrue(isAnnotationPresent(super.testTypeElement, Component.class)); + assertTrue(isAnnotationPresent(super.testTypeElement, ServiceMode.class)); + assertTrue(isAnnotationPresent(super.testTypeElement, Inherited.class)); + assertTrue(isAnnotationPresent(super.testTypeElement, Documented.class)); } @Test void testIsAnnotationPresentOnAnnotationClassOnNull() { assertFalse(isAnnotationPresent(NULL_ELEMENT, Service.class)); - assertFalse(isAnnotationPresent(testTypeElement, NULL_CLASS)); - assertFalse(isAnnotationPresent(testTypeElement, Override.class)); + assertFalse(isAnnotationPresent(super.testTypeElement, NULL_CLASS)); + assertFalse(isAnnotationPresent(super.testTypeElement, Override.class)); } @Test void testIsAnnotationPresentOnAnnotationClassName() { - assertTrue(isAnnotationPresent(testTypeElement, "org.springframework.stereotype.Service")); - assertTrue(isAnnotationPresent(testTypeElement, "org.springframework.stereotype.Component")); - assertTrue(isAnnotationPresent(testTypeElement, "javax.xml.ws.ServiceMode")); - assertTrue(isAnnotationPresent(testTypeElement, "java.lang.annotation.Inherited")); - assertTrue(isAnnotationPresent(testTypeElement, "java.lang.annotation.Documented")); + assertTrue(isAnnotationPresent(super.testTypeElement, "org.springframework.stereotype.Service")); + assertTrue(isAnnotationPresent(super.testTypeElement, "org.springframework.stereotype.Component")); + assertTrue(isAnnotationPresent(super.testTypeElement, "javax.xml.ws.ServiceMode")); + assertTrue(isAnnotationPresent(super.testTypeElement, "java.lang.annotation.Inherited")); + assertTrue(isAnnotationPresent(super.testTypeElement, "java.lang.annotation.Documented")); } @Test void testIsAnnotationPresentOnAnnotationClassNameOnNull() { assertFalse(isAnnotationPresent(NULL_ELEMENT, "org.springframework.stereotype.Service")); - assertFalse(isAnnotationPresent(testTypeElement, NULL_STRING)); - assertFalse(isAnnotationPresent(testTypeElement, "java.lang.Override")); + assertFalse(isAnnotationPresent(super.testTypeElement, NULL_STRING)); + assertFalse(isAnnotationPresent(super.testTypeElement, "java.lang.Override")); } @Test void testFindAnnotations() { - List annotations = findAnnotations(testTypeElement); + List annotations = findAnnotations(super.testTypeElement); assertEquals(4, annotations.size()); assertAnnotation(annotations.get(0), Service.class); assertAnnotation(annotations.get(1), ServiceMode.class); assertAnnotation(annotations.get(2), ComponentScans.class); assertAnnotation(annotations.get(3), TestAnnotation.class); - annotations = findAnnotations(testTypeElement, alwaysTrue()); + annotations = findAnnotations(super.testTypeElement, alwaysTrue()); assertEquals(4, annotations.size()); assertAnnotation(annotations.get(0), Service.class); assertAnnotation(annotations.get(1), ServiceMode.class); assertAnnotation(annotations.get(2), ComponentScans.class); assertAnnotation(annotations.get(3), TestAnnotation.class); - annotations = findAnnotations(testTypeElement, alwaysFalse()); + annotations = findAnnotations(super.testTypeElement, alwaysFalse()); assertEmptyList(annotations); } @@ -518,7 +520,7 @@ void testFindAnnotationsOnNull() { @Test void testGetAttributeName() { - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + Map elementValues = getElementValues(super.testTypeElement, TestAnnotation.class); for (Entry entry : elementValues.entrySet()) { ExecutableElement attributeMethod = entry.getKey(); assertEquals(attributeMethod.getSimpleName().toString(), getAttributeName(attributeMethod)); @@ -527,7 +529,7 @@ void testGetAttributeName() { @Test void testMatchesAttributeMethod() { - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + Map elementValues = getElementValues(super.testTypeElement, TestAnnotation.class); for (Entry entry : elementValues.entrySet()) { ExecutableElement attributeMethod = entry.getKey(); assertTrue(matchesAttributeMethod(attributeMethod, getAttributeName(attributeMethod))); @@ -538,7 +540,7 @@ void testMatchesAttributeMethod() { void testMatchesAttributeMethodOnNull() { assertFalse(matchesAttributeMethod(null, null)); - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + Map elementValues = getElementValues(super.testTypeElement, TestAnnotation.class); for (Entry entry : elementValues.entrySet()) { ExecutableElement attributeMethod = entry.getKey(); assertFalse(matchesAttributeMethod(attributeMethod, null)); @@ -547,7 +549,7 @@ void testMatchesAttributeMethodOnNull() { @Test void testMatchesAttributeValue() { - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + Map elementValues = getElementValues(super.testTypeElement, TestAnnotation.class); for (Entry entry : elementValues.entrySet()) { AnnotationValue annotationValue = entry.getValue(); assertTrue(matchesAttributeValue(annotationValue, annotationValue)); @@ -562,7 +564,7 @@ void testMatchesAttributeValueOnNull() { assertTrue(matchesAttributeValue(null, null)); assertFalse(matchesAttributeValue(null, (Object) null)); - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + Map elementValues = getElementValues(super.testTypeElement, TestAnnotation.class); for (Entry entry : elementValues.entrySet()) { AnnotationValue annotationValue = entry.getValue(); assertFalse(matchesAttributeValue(annotationValue, null)); @@ -572,16 +574,25 @@ void testMatchesAttributeValueOnNull() { @Test void testMatchesDefaultAttributeValue() { - Map elementValues = getElementValues(testTypeElement, ServiceMode.class); + Map elementValues = getElementValues(super.testTypeElement, ServiceMode.class); for (Entry entry : elementValues.entrySet()) { ExecutableElement attributeMethod = entry.getKey(); assertTrue(matchesDefaultAttributeValue(attributeMethod, attributeMethod.getDefaultValue())); } } + @Test + void testMatchesDefaultAttributeValueOnNull() { + Map elementValues = getElementValues(super.testTypeElement, ServiceMode.class); + for (Entry entry : elementValues.entrySet()) { + ExecutableElement attributeMethod = entry.getKey(); + assertFalse(matchesDefaultAttributeValue(NULL_METHOD, attributeMethod.getDefaultValue())); + } + } + @Test void testGetElementValue() { - Map elementValues = getElementValues(testTypeElement, TestAnnotation.class); + Map elementValues = getElementValues(super.testTypeElement, TestAnnotation.class); for (Entry entry : elementValues.entrySet()) { ExecutableElement attributeMethod = entry.getKey(); String attributeName = getAttributeName(attributeMethod); @@ -589,11 +600,29 @@ void testGetElementValue() { } assertNull(getElementValue(elementValues, "unknown")); + + AnnotationMirror annotation = findAnnotation(super.testTypeElement, ServiceMode.class); + Entry elementValue = getElementValue(annotation, "value", true); + + assertAttributeEntry(elementValue, "value", PAYLOAD); + + elementValue = getElementValue(annotation, "value", false); + assertNull(elementValue); + } + + @Test + void testGetElementValueOnNotFound() { + AnnotationMirror annotation = findAnnotation(super.testTypeElement, ServiceMode.class); + Entry elementValue = getElementValue(annotation, "z", true); + assertNull(elementValue); + + elementValue = getElementValue(annotation, "z", false); + assertNull(elementValue); } @Test void testGetElementValueOnEmptyElementValues() { - AnnotationMirror annotation = findAnnotation(testTypeElement, ServiceMode.class); + AnnotationMirror annotation = findAnnotation(super.testTypeElement, ServiceMode.class); Map elementValues = annotation.getElementValues(); assertNull(getElementValue(elementValues, "value")); } @@ -605,14 +634,14 @@ void testGetElementValueOnNull() { @Test void testGetElementValuesMapOnAnnotatedClass() { - Map attributesMap = getAttributesMap(testTypeElement, Service.class); + Map attributesMap = getAttributesMap(super.testTypeElement, Service.class); assertEquals(1, attributesMap.size()); assertEquals("testService", attributesMap.get("value")); } @Test void testGetElementValuesMapOnAnnotatedMethod() { - ExecutableElement method = findMethod(testTypeElement, "echo", String.class); + ExecutableElement method = findMethod(super.testTypeElement, "echo", String.class); Map attributesMap = getAttributesMap(method, Cacheable.class); assertEquals(9, attributesMap.size()); assertArrayEquals(ofArray("cache-1", "cache-2"), (String[]) attributesMap.get("cacheNames")); @@ -620,7 +649,7 @@ void testGetElementValuesMapOnAnnotatedMethod() { @Test void testGetElementValuesMapOnRepeatableAnnotation() { - Map attributesMap = getAttributesMap(testTypeElement, ComponentScans.class); + Map attributesMap = getAttributesMap(super.testTypeElement, ComponentScans.class); assertEquals(1, attributesMap.size()); ComponentScans componentScans = testClass.getAnnotation(ComponentScans.class); @@ -634,7 +663,7 @@ void testGetElementValuesMapOnNull() { Map attributesMap = getAttributesMap(null, null); assertSame(emptyMap(), attributesMap); - attributesMap = getAttributesMap(testTypeElement, null); + attributesMap = getAttributesMap(super.testTypeElement, null); assertSame(emptyMap(), attributesMap); attributesMap = getAttributesMap(null); @@ -643,16 +672,16 @@ void testGetElementValuesMapOnNull() { @Test void testGetElementValuesOnAnnotatedClass() { - Map elementValues = getElementValues(testTypeElement, Service.class); + Map elementValues = getElementValues(super.testTypeElement, Service.class); assertServiceAttributes(elementValues); - elementValues = getElementValues(testTypeElement, Service.class, false); + elementValues = getElementValues(super.testTypeElement, Service.class, false); assertServiceAttributes(elementValues); } @Test void testGetElementValuesOnAnnotatedMethod() { - ExecutableElement method = findMethod(testTypeElement, "echo", String.class); + ExecutableElement method = findMethod(super.testTypeElement, "echo", String.class); Map elementValues = getElementValues(method, Cacheable.class, false); assertEquals(1, elementValues.size()); assertAttributeEntry(elementValues, "cacheNames", ofArray("cache-1", "cache-2")); @@ -686,7 +715,7 @@ void testGetElementTypes() { } void assertElementTypes(Class annotationClass, ElementType... expectedElementTypes) { - AnnotationMirror annotationMirror = findAnnotation(this.testTypeElement, annotationClass); + AnnotationMirror annotationMirror = findAnnotation(super.testTypeElement, annotationClass); assertArrayEquals(expectedElementTypes, getElementTypes(annotationMirror)); } @@ -711,19 +740,26 @@ void assertAttributeEntry(Map attributes, St } } - void assertAttributeEntry(Entry attributeEntry, String attributeName, Object attributeValue) { + void assertAttributeEntry(Entry attributeEntry, String attributeName, Object expectedAttributeValue) { + assertNotNull(attributeEntry); + ExecutableElement attributeMethod = attributeEntry.getKey(); - AnnotationValue annotationValue = attributeEntry.getValue(); + AnnotationValue attributeValue = attributeEntry.getValue(); + assertNotNull(attributeMethod); + assertNotNull(attributeValue); + assertEquals(attributeName, getAttributeName(attributeMethod)); + assertTrue(matchesAttributeMethod(attributeMethod, getAttributeName(attributeMethod))); + Object value = getAttribute(attributeEntry); Class attributeValueClass = value.getClass(); if (attributeValueClass.isArray()) { Class componentType = attributeValueClass.getComponentType(); if (String.class.equals(componentType)) { - assertArrayEquals((String[]) attributeValue, (String[]) value); + assertArrayEquals((String[]) expectedAttributeValue, (String[]) value); } } else { - assertEquals(attributeValue, value); + assertEquals(expectedAttributeValue, value); } } @@ -736,30 +772,30 @@ private void assertFindMetaAnnotation(Element element, String annotationClassNam } private void assertFindAnnotation(Class annotationClass) { - assertAnnotation(findAnnotation(testTypeMirror, annotationClass), annotationClass); - assertAnnotation(findAnnotation(testTypeElement, annotationClass), annotationClass); - assertAnnotation(findAnnotation(testTypeMirror, annotationClass.getName()), annotationClass); - assertAnnotation(findAnnotation(testTypeElement, annotationClass.getName()), annotationClass); + assertAnnotation(findAnnotation(super.testTypeMirror, annotationClass), annotationClass); + assertAnnotation(findAnnotation(super.testTypeElement, annotationClass), annotationClass); + assertAnnotation(findAnnotation(super.testTypeMirror, annotationClass.getName()), annotationClass); + assertAnnotation(findAnnotation(super.testTypeElement, annotationClass.getName()), annotationClass); } private void asserGetAnnotation(Class annotationClass) { - AnnotationMirror annotation = getAnnotation(testTypeElement, annotationClass); + AnnotationMirror annotation = getAnnotation(super.testTypeElement, annotationClass); assertAnnotation(annotation, annotationClass); } private void asserGetAnnotation(String annotationClassName) { - AnnotationMirror annotation = getAnnotation(testTypeElement, annotationClassName); + AnnotationMirror annotation = getAnnotation(super.testTypeElement, annotationClassName); assertAnnotation(annotation, annotationClassName); } private void assertGetAnnotations(Class annotationClass) { - List annotations = getAnnotations(testTypeElement, annotationClass); + List annotations = getAnnotations(super.testTypeElement, annotationClass); assertEquals(1, annotations.size()); assertAnnotation(annotations.get(0), annotationClass); } private void assertGetAnnotations(String annotationClassName) { - List annotations = getAnnotations(testTypeElement, annotationClassName); + List annotations = getAnnotations(super.testTypeElement, annotationClassName); assertEquals(1, annotations.size()); assertAnnotation(annotations.get(0), annotationClassName); } From 5dfc61daa90c054330bf6eedb51e983595966e10 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 18:28:30 +0800 Subject: [PATCH 057/438] Add null-arg assertion to AnnotationUtilsTest Add an assertion to verify matchesAttributeValue returns false when the first argument is null and the second is a non-null AnnotationValue. Strengthens null-safety coverage in AnnotationUtils tests. --- .../java/io/microsphere/lang/model/util/AnnotationUtilsTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java index bb9a48f9b..5b01b35da 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotationUtilsTest.java @@ -569,6 +569,7 @@ void testMatchesAttributeValueOnNull() { AnnotationValue annotationValue = entry.getValue(); assertFalse(matchesAttributeValue(annotationValue, null)); assertFalse(matchesAttributeValue(annotationValue, (Object) null)); + assertFalse(matchesAttributeValue(null, annotationValue)); } } From 55628e2c39d69cfe9a889df8bf731c42e5c5558e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 18:39:02 +0800 Subject: [PATCH 058/438] Null-check declaredType in isTypeElement Add an explicit null check and early return in TypeUtils.isTypeElement to avoid calling declaredType.asElement() when declaredType is null. This prevents a potential NPE and improves readability of the method. --- .../main/java/io/microsphere/lang/model/util/TypeUtils.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java index 06692f441..1da76f704 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/TypeUtils.java @@ -579,7 +579,10 @@ static boolean isTypeElement(Element element) { */ static boolean isTypeElement(TypeMirror type) { DeclaredType declaredType = ofDeclaredType(type); - return declaredType != null && isTypeElement(declaredType.asElement()); + if (declaredType == null) { + return false; + } + return isTypeElement(declaredType.asElement()); } /** From 6b9a35e514229611a22422249fe47770460b73e5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 18:41:53 +0800 Subject: [PATCH 059/438] Make support checks explicit in JSONElementVisitor Replace inline logical-and guard returns with explicit if-checks in visitPackage, visitVariable, visitExecutable and visitTypeParameter. Behavior is unchanged; this refactor improves readability and makes it easier to set breakpoints during debugging. --- .../lang/model/util/JSONElementVisitor.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java index 46cf974e0..416eafce3 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/JSONElementVisitor.java @@ -62,17 +62,26 @@ public JSONElementVisitor() { @Override public final Boolean visitPackage(PackageElement e, StringBuilder jsonBuilder) { - return supportsPackage(e) && doVisitPackage(e, jsonBuilder); + if (!supportsPackage(e)) { + return false; + } + return doVisitPackage(e, jsonBuilder); } @Override public final Boolean visitVariable(VariableElement e, StringBuilder stringBuilder) { - return supportsVariable(e) && super.visitVariable(e, stringBuilder); + if (!supportsVariable(e)) { + return false; + } + return super.visitVariable(e, stringBuilder); } @Override public final Boolean visitExecutable(ExecutableElement e, StringBuilder jsonBuilder) { - return supportsExecutable(e) && super.visitExecutable(e, jsonBuilder); + if (!supportsExecutable(e)) { + return false; + } + return super.visitExecutable(e, jsonBuilder); } @Override @@ -93,7 +102,10 @@ public final Boolean visitType(TypeElement e, StringBuilder jsonBuilder) { @Override public final Boolean visitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { - return supportsTypeParameter(e) && doVisitTypeParameter(e, jsonBuilder); + if (!supportsTypeParameter(e)) { + return false; + } + return doVisitTypeParameter(e, jsonBuilder); } protected boolean visitMembers(List members, StringBuilder jsonBuilder) { @@ -205,4 +217,4 @@ protected boolean doVisitPackage(PackageElement e, StringBuilder jsonBuilder) { protected boolean doVisitTypeParameter(TypeParameterElement e, StringBuilder jsonBuilder) { return super.visitTypeParameter(e, jsonBuilder); } -} +} \ No newline at end of file From 86af0347591458a1408395454f65ad6186277566 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 18:47:22 +0800 Subject: [PATCH 060/438] Explicit null check in matchesDefaultAttributeValue Replace the inline null-check return with an explicit if-block in AnnotationUtils.matchesDefaultAttributeValue to improve readability. The method now returns false immediately if attributeMethod is null, then delegates to matchesAttributeValue for the default value; no behavioral change intended. --- .../java/io/microsphere/lang/model/util/AnnotationUtils.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java index 816854717..ea4859b76 100644 --- a/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotationUtils.java @@ -1142,7 +1142,10 @@ static boolean matchesAttributeValue(AnnotationValue annotationValue, Object att * {@code false} otherwise */ static boolean matchesDefaultAttributeValue(ExecutableElement attributeMethod, AnnotationValue annotationValue) { - return attributeMethod != null && matchesAttributeValue(attributeMethod.getDefaultValue(), annotationValue); + if (attributeMethod == null) { + return false; + } + return matchesAttributeValue(attributeMethod.getDefaultValue(), annotationValue); } /** From b52a110f6940121fbf531ff8021af4c5b2cc8f44 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 19:02:56 +0800 Subject: [PATCH 061/438] Use EMPTY_CLASS_ARRAY and pass class array Refactor CompilerInvocationInterceptor to build a Class[] from the collected Set and pass that array to Compiler APIs. Added a static import of EMPTY_CLASS_ARRAY and renamed the Set to compiledClassesSet to avoid naming confusion. This ensures typed empty-array usage with toArray(EMPTY_CLASS_ARRAY) and uses the resulting Class[] for sourcePaths(...) and compile(...), removing ad-hoc toArray(new Class[0]) calls. --- .../processing/CompilerInvocationInterceptor.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java index a0d113350..c45776142 100644 --- a/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java +++ b/microsphere-java-test/src/main/java/io/microsphere/test/annotation/processing/CompilerInvocationInterceptor.java @@ -29,6 +29,7 @@ import java.util.ServiceLoader; import java.util.Set; +import static io.microsphere.util.ArrayUtils.EMPTY_CLASS_ARRAY; import static java.util.ServiceLoader.load; @@ -43,14 +44,17 @@ class CompilerInvocationInterceptor implements InvocationInterceptor { @Override public void interceptTestMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { - Set> compiledClasses = new LinkedHashSet<>(); + Set> compiledClassesSet = new LinkedHashSet<>(); AbstractAnnotationProcessingTest test = (AbstractAnnotationProcessingTest) invocationContext.getTarget().get(); Class testClass = extensionContext.getTestClass().get(); ClassLoader classLoader = testClass.getClassLoader(); - compiledClasses.add(testClass); - test.addCompiledClasses(compiledClasses); + compiledClassesSet.add(testClass); + test.addCompiledClasses(compiledClassesSet); + + Class[] compiledClasses = compiledClassesSet.toArray(EMPTY_CLASS_ARRAY); + Compiler compiler = new Compiler(); - compiler.sourcePaths(testClass); + compiler.sourcePaths(compiledClasses); List processors = new LinkedList<>(); processors.add(new AnnotationProcessingTestProcessor(test, invocation, invocationContext, extensionContext)); @@ -58,6 +62,6 @@ public void interceptTestMethod(Invocation invocation, ReflectiveInvocatio ServiceLoader loadedProcessors = load(Processor.class, classLoader); loadedProcessors.forEach(processors::add); compiler.processors(processors.toArray(new Processor[0])); - compiler.compile(compiledClasses.toArray(new Class[0])); + compiler.compile(compiledClasses); } } \ No newline at end of file From 5fada1fff1b987a241d0ccfe21f6fa214e7ac759 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 20:10:54 +0800 Subject: [PATCH 062/438] Refactor JSON visitors and processor, add tests Move AnnotatedElementJSONElementVisitor to microsphere-lang-model and update packages/usages; make ConfigurationPropertyJSONElementVisitor package-private, change its CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME and constructor visibility, and override supportsType to always return true. Update ConfigurationPropertyAnnotationProcessor to import ResourceConstants, adjust static import path, refactor resolveMetadata into an overload that accepts a Set (prepend '[' before resolving, append generated SPI JSON, and ensure proper trailing bracket handling), and add a toJSON() helper. Update and add unit tests to match the refactor: adapt ConfigurationPropertyAnnotationProcessorTest, add ConfigurationPropertyJSONElementVisitorTest, and add AnnotatedElementJSONElementVisitorTest under the lang-model tests. Miscellaneous import and formatting adjustments applied to affected files. --- ...figurationPropertyAnnotationProcessor.java | 33 +++++---- ...nfigurationPropertyJSONElementVisitor.java | 22 +++--- ...rationPropertyAnnotationProcessorTest.java | 31 +++------ ...urationPropertyJSONElementVisitorTest.java | 68 +++++++++++++++++++ .../AnnotatedElementJSONElementVisitor.java | 4 +- ...nnotatedElementJSONElementVisitorTest.java | 52 ++++++++++++++ 6 files changed, 163 insertions(+), 47 deletions(-) rename microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/{model/util => }/ConfigurationPropertyJSONElementVisitor.java (92%) create mode 100644 microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitorTest.java rename {microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor => microsphere-lang-model/src/main/java/io/microsphere/lang}/model/util/AnnotatedElementJSONElementVisitor.java (97%) create mode 100644 microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotatedElementJSONElementVisitorTest.java diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessor.java index 6060855d6..4523a1e42 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessor.java @@ -18,7 +18,7 @@ package io.microsphere.annotation.processor; import io.microsphere.annotation.ConfigurationProperty; -import io.microsphere.annotation.processor.model.util.ConfigurationPropertyJSONElementVisitor; +import io.microsphere.constants.ResourceConstants; import io.microsphere.json.JSONArray; import io.microsphere.metadata.ConfigurationPropertyGenerator; @@ -35,7 +35,7 @@ import java.util.List; import java.util.Set; -import static io.microsphere.annotation.processor.model.util.ConfigurationPropertyJSONElementVisitor.CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME; +import static io.microsphere.annotation.processor.ConfigurationPropertyJSONElementVisitor.CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME; import static io.microsphere.constants.ResourceConstants.CONFIGURATION_PROPERTY_METADATA_RESOURCE; import static io.microsphere.constants.SymbolConstants.COMMA_CHAR; import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET_CHAR; @@ -62,7 +62,7 @@ * *

  • {@link #resolveMetadata(RoundEnvironment)} traverses all root elements to extract configuration property metadata.
  • *
  • {@link #writeMetadata()} writes the generated metadata into a JSON file under - * {@value #CONFIGURATION_PROPERTY_METADATA_RESOURCE_NAME} using a writer.
  • + * {@value ResourceConstants#CONFIGURATION_PROPERTY_METADATA_RESOURCE} using a writer. * * * @author Mercy @@ -106,24 +106,28 @@ public boolean process(Set annotations, RoundEnvironment private void resolveMetadata(RoundEnvironment roundEnv) { Set elements = roundEnv.getRootElements(); - if (!elements.isEmpty()) { + resolveMetadata(elements); + } + + void resolveMetadata(Set elements) { + jsonBuilder.append(LEFT_SQUARE_BRACKET_CHAR); + // Resolve the content + if (!elements.isEmpty()) { Iterator iterator = elements.iterator(); - jsonBuilder.append(LEFT_SQUARE_BRACKET_CHAR); while (iterator.hasNext()) { Element element = iterator.next(); element.accept(jsonElementVisitor, jsonBuilder); } - // append the JSON content generated by ConfigurationPropertyGenerator SPI appendGeneratedConfigurationPropertyJSON(jsonBuilder); + } - int lastIndex = jsonBuilder.length() - 1; - if (COMMA_CHAR == jsonBuilder.charAt(lastIndex)) { - jsonBuilder.setCharAt(lastIndex, RIGHT_SQUARE_BRACKET_CHAR); - } else { - jsonBuilder.append(RIGHT_SQUARE_BRACKET_CHAR); - } + int lastIndex = jsonBuilder.length() - 1; + if (COMMA_CHAR == jsonBuilder.charAt(lastIndex)) { + jsonBuilder.setCharAt(lastIndex, RIGHT_SQUARE_BRACKET_CHAR); + } else { + jsonBuilder.append(RIGHT_SQUARE_BRACKET_CHAR); } } @@ -151,9 +155,12 @@ private void writeMetadata() { }); } + String toJSON() { + return jsonBuilder.toString(); + } + @Override public SourceVersion getSupportedSourceVersion() { return latestSupported(); } } - \ No newline at end of file diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitor.java similarity index 92% rename from microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java rename to microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitor.java index 5f5b9f2f7..bd8a2463c 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/ConfigurationPropertyJSONElementVisitor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitor.java @@ -15,10 +15,11 @@ * limitations under the License. */ -package io.microsphere.annotation.processor.model.util; +package io.microsphere.annotation.processor; import io.microsphere.annotation.ConfigurationProperty; import io.microsphere.beans.ConfigurationProperty.Metadata; +import io.microsphere.lang.model.util.AnnotatedElementJSONElementVisitor; import io.microsphere.metadata.ConfigurationPropertyGenerator; import javax.annotation.processing.ProcessingEnvironment; @@ -26,6 +27,7 @@ import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.util.List; @@ -42,22 +44,22 @@ import static io.microsphere.util.StringUtils.isBlank; /** - * {@link ConfigurationProperty @ConfigurationProperty}'s {@link io.microsphere.lang.model.util.AnnotatedElementJSONElementVisitor} based on + * {@link ConfigurationProperty @ConfigurationProperty}'s {@link AnnotatedElementJSONElementVisitor} based on * {@link ConfigurationPropertyGenerator} generating the JSON representation of the configuration property metadata. * * @author Mercy - * @see io.microsphere.lang.model.util.AnnotatedElementJSONElementVisitor + * @see AnnotatedElementJSONElementVisitor * @see ConfigurationProperty * @see io.microsphere.beans.ConfigurationProperty * @since 1.0.0 */ -public class ConfigurationPropertyJSONElementVisitor extends AnnotatedElementJSONElementVisitor { +class ConfigurationPropertyJSONElementVisitor extends AnnotatedElementJSONElementVisitor { - public static final String CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME = "io.microsphere.annotation.ConfigurationProperty"; + static final String CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME = "io.microsphere.annotation.ConfigurationProperty"; private final ConfigurationPropertyGenerator generator; - public ConfigurationPropertyJSONElementVisitor(ProcessingEnvironment processingEnv) { + ConfigurationPropertyJSONElementVisitor(ProcessingEnvironment processingEnv) { super(processingEnv, CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME); this.generator = loadFirstService(ConfigurationPropertyGenerator.class); } @@ -102,6 +104,11 @@ public Boolean visitVariableAsField(VariableElement field, StringBuilder jsonBui return false; } + @Override + protected boolean supportsType(TypeElement e) { + return true; + } + public ConfigurationPropertyGenerator getGenerator() { return generator; } @@ -149,5 +156,4 @@ private void setDeclaredField(io.microsphere.beans.ConfigurationProperty configu String declaredFieldName = field.getSimpleName().toString(); configurationProperty.getMetadata().setDeclaredField(declaredFieldName); } - -} +} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessorTest.java index 2fd190bc9..2e2806fa0 100644 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessorTest.java +++ b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyAnnotationProcessorTest.java @@ -17,20 +17,11 @@ package io.microsphere.annotation.processor; -import io.microsphere.annotation.ConfigurationProperty; -import io.microsphere.classloading.ManifestArtifactResourceResolver; -import io.microsphere.io.IOUtils; -import io.microsphere.io.StandardFileWatchService; -import io.microsphere.reflect.MethodUtils; -import io.microsphere.reflect.TypeUtils; import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; -import io.microsphere.util.ServiceLoaderUtils; import org.junit.jupiter.api.Test; -import java.util.Set; - -import static io.microsphere.annotation.processor.model.util.ConfigurationPropertyJSONElementVisitor.CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static io.microsphere.util.Assert.assertNotNull; +import static java.util.Collections.emptySet; /** * {@link ConfigurationPropertyAnnotationProcessor} Test @@ -40,19 +31,13 @@ * @since 1.0.0 */ class ConfigurationPropertyAnnotationProcessorTest extends AbstractAnnotationProcessingTest { - @Override - protected void addCompiledClasses(Set> compiledClasses) { - compiledClasses.add(ManifestArtifactResourceResolver.class); - compiledClasses.add(IOUtils.class); - compiledClasses.add(StandardFileWatchService.class); - compiledClasses.add(TypeUtils.class); - compiledClasses.add(ServiceLoaderUtils.class); - compiledClasses.add(MethodUtils.class); - compiledClasses.add(ConfigurationProperty.class); - } @Test - void testConstants() { - assertEquals("io.microsphere.annotation.ConfigurationProperty", CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME); + void testResolveMetadataOnEmptySet() { + ConfigurationPropertyAnnotationProcessor processor = new ConfigurationPropertyAnnotationProcessor(); + processor.init(super.processingEnv); + processor.resolveMetadata(emptySet()); + String json = processor.toJSON(); + assertNotNull("[]", json); } } \ No newline at end of file diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitorTest.java new file mode 100644 index 000000000..8f06ccce4 --- /dev/null +++ b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitorTest.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.microsphere.annotation.processor; + + +import io.microsphere.annotation.ConfigurationProperty; +import io.microsphere.classloading.ManifestArtifactResourceResolver; +import io.microsphere.io.IOUtils; +import io.microsphere.io.StandardFileWatchService; +import io.microsphere.reflect.MethodUtils; +import io.microsphere.reflect.TypeUtils; +import io.microsphere.test.annotation.processing.AbstractAnnotationProcessingTest; +import io.microsphere.util.ServiceLoaderUtils; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static io.microsphere.annotation.processor.ConfigurationPropertyJSONElementVisitor.CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link ConfigurationPropertyJSONElementVisitor} Test + * + * @author Mercy + * @see ConfigurationPropertyJSONElementVisitor + * @since 1.0.0 + */ +class ConfigurationPropertyJSONElementVisitorTest extends AbstractAnnotationProcessingTest { + + @Override + protected void addCompiledClasses(Set> compiledClasses) { + compiledClasses.add(ManifestArtifactResourceResolver.class); + compiledClasses.add(IOUtils.class); + compiledClasses.add(StandardFileWatchService.class); + compiledClasses.add(TypeUtils.class); + compiledClasses.add(ServiceLoaderUtils.class); + compiledClasses.add(MethodUtils.class); + compiledClasses.add(ConfigurationProperty.class); + } + + @Test + void testConstants() { + assertEquals("io.microsphere.annotation.ConfigurationProperty", CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME); + } + + @Test + void testSupportsType() { + ConfigurationPropertyJSONElementVisitor visitor = new ConfigurationPropertyJSONElementVisitor(super.processingEnv); + assertTrue(visitor.supportsType(super.testTypeElement)); + assertTrue(visitor.supportsType(NULL_TYPE_ELEMENT)); + } +} \ No newline at end of file diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/AnnotatedElementJSONElementVisitor.java b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotatedElementJSONElementVisitor.java similarity index 97% rename from microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/AnnotatedElementJSONElementVisitor.java rename to microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotatedElementJSONElementVisitor.java index cbb1d6418..e6df266ec 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/model/util/AnnotatedElementJSONElementVisitor.java +++ b/microsphere-lang-model/src/main/java/io/microsphere/lang/model/util/AnnotatedElementJSONElementVisitor.java @@ -15,10 +15,9 @@ * limitations under the License. */ -package io.microsphere.annotation.processor.model.util; +package io.microsphere.lang.model.util; import io.microsphere.annotation.Nonnull; -import io.microsphere.lang.model.util.JSONElementVisitor; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; @@ -125,5 +124,4 @@ public final String getAnnotationClassName() { protected boolean supports(Element e) { return matchesElementType(e, this.elementTypes); } - } diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotatedElementJSONElementVisitorTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotatedElementJSONElementVisitorTest.java new file mode 100644 index 000000000..3dd0cc74c --- /dev/null +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/AnnotatedElementJSONElementVisitorTest.java @@ -0,0 +1,52 @@ +/* + * 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. + */ + +package io.microsphere.lang.model.util; + + +import org.junit.jupiter.api.Test; + +import javax.lang.model.element.ExecutableElement; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link AnnotatedElementJSONElementVisitor} Test + * + * @author Mercy + * @see AnnotatedElementJSONElementVisitor + * @since 1.0.0 + */ +class AnnotatedElementJSONElementVisitorTest extends UtilTest { + + @Test + void test() { + String annotationClassName = Test.class.getName(); + AnnotatedElementJSONElementVisitor visitor = new AnnotatedElementJSONElementVisitor(super.processingEnv, annotationClassName) { + }; + + assertEquals(annotationClassName, visitor.getAnnotationClassName()); + + ExecutableElement testMethod = getMethod(AnnotatedElementJSONElementVisitorTest.class, "test"); + assertTrue(visitor.supports(testMethod)); + assertFalse(visitor.supports(super.testTypeElement)); + assertFalse(visitor.supports(NULL_ELEMENT)); + } + +} \ No newline at end of file From a0f23ddcc40b7ce915d834c0027c7a0778a3db1d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 20:39:04 +0800 Subject: [PATCH 063/438] Check attribute name in setSources Pass the attribute name into setSources and make the method process only when attributeName equals "source". This avoids treating non-source annotation attributes as sources when the visitor's default branch invokes setSources. The method visibility was relaxed and a unit test (testSetSourcesOnNoSource) was added to ensure calling setSources with a non-'source' attribute and null values does not throw. --- ...onfigurationPropertyJSONElementVisitor.java | 18 ++++++++++-------- ...gurationPropertyJSONElementVisitorTest.java | 8 ++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitor.java b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitor.java index bd8a2463c..e30972026 100644 --- a/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitor.java +++ b/microsphere-annotation-processor/src/main/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitor.java @@ -89,8 +89,8 @@ public Boolean visitVariableAsField(VariableElement field, StringBuilder jsonBui } else if ("description".equals(attributeName)) { String description = resolveDescription(field, attributeMethod, annotationValue); configurationProperty.setDescription(description); - } else if ("source".equals(attributeName)) { - setSources(configurationProperty, annotationValue); + } else { + setSources(configurationProperty, attributeName, annotationValue); } } setDeclaredClass(configurationProperty, field); @@ -137,12 +137,14 @@ private String resolveStringValue(ExecutableElement attributeMethod, AnnotationV return (String) value; } - private void setSources(io.microsphere.beans.ConfigurationProperty configurationProperty, AnnotationValue annotationValue) { - List sources = (List) annotationValue.getValue(); - Metadata metadata = configurationProperty.getMetadata(); - for (AnnotationValue source : sources) { - String sourceValue = (String) source.getValue(); - metadata.getSources().add(sourceValue); + void setSources(io.microsphere.beans.ConfigurationProperty configurationProperty, String attributeName, AnnotationValue annotationValue) { + if ("source".equals(attributeName)) { + List sources = (List) annotationValue.getValue(); + Metadata metadata = configurationProperty.getMetadata(); + for (AnnotationValue source : sources) { + String sourceValue = (String) source.getValue(); + metadata.getSources().add(sourceValue); + } } } diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitorTest.java index 8f06ccce4..89e4e5deb 100644 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitorTest.java +++ b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ConfigurationPropertyJSONElementVisitorTest.java @@ -32,6 +32,7 @@ import static io.microsphere.annotation.processor.ConfigurationPropertyJSONElementVisitor.CONFIGURATION_PROPERTY_ANNOTATION_CLASS_NAME; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -65,4 +66,11 @@ void testSupportsType() { assertTrue(visitor.supportsType(super.testTypeElement)); assertTrue(visitor.supportsType(NULL_TYPE_ELEMENT)); } + + @Test + void testSetSourcesOnNoSource() { + ConfigurationPropertyJSONElementVisitor visitor = new ConfigurationPropertyJSONElementVisitor(super.processingEnv); + visitor.setSources(null, "noSource", null); + assertNotNull(visitor); + } } \ No newline at end of file From 3ac0646e1cc0255a88aa4bf527cfee3391af6854 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 20:55:57 +0800 Subject: [PATCH 064/438] Add exception-handler tests for resource processors Extend ResourceProcessorTest to cover fallback/error-handler behavior when resource callbacks throw. Add assertions that: - classOutputProcessor.processInResource with a throwing callback returns null when a handler returning null is provided. - sourcePathProcessor.processInResourceInputStream/Reader/Content with throwing callbacks return Optional.empty() when a handler returning null is provided. - classOutputProcessor.processInResourceOutputStream and processInResourceWriter invoke the error handler and receive a non-null exception. These tests ensure resource processors correctly propagate exceptions to provided handlers and return the expected fallback values. --- .../processor/ResourceProcessorTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ResourceProcessorTest.java b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ResourceProcessorTest.java index 868022463..1ed22a16d 100644 --- a/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ResourceProcessorTest.java +++ b/microsphere-annotation-processor/src/test/java/io/microsphere/annotation/processor/ResourceProcessorTest.java @@ -34,11 +34,14 @@ import static io.microsphere.nio.charset.CharsetUtils.DEFAULT_CHARSET; import static java.lang.Boolean.FALSE; import static java.lang.System.currentTimeMillis; +import static java.util.Optional.empty; import static javax.tools.StandardLocation.CLASS_OUTPUT; import static javax.tools.StandardLocation.SOURCE_PATH; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -81,6 +84,10 @@ void testProcessInResourceOnFailed() { assertThrows(RuntimeException.class, () -> this.classOutputProcessor.processInResource(this.randomResourceName, FOR_READING, fileObject -> { throw new RuntimeException(); })); + + assertNull(this.classOutputProcessor.processInResource(this.randomResourceName, FOR_READING, fileObject -> { + throw new RuntimeException(); + }, e -> null)); } @Test @@ -101,6 +108,10 @@ void testProcessInResourceInputStreamOnFailed() { assertThrows(RuntimeException.class, () -> this.sourcePathProcessor.processInResourceInputStream(JAVA_SOURCE_RESOURCE_NAME, inputStream -> { throw new RuntimeException(); })); + + assertSame(empty(), this.sourcePathProcessor.processInResourceInputStream(JAVA_SOURCE_RESOURCE_NAME, inputStream -> { + throw new RuntimeException(); + }, (f, e) -> null)); } @Test @@ -115,6 +126,10 @@ void testProcessInResourceReaderOnFailed() { assertThrows(RuntimeException.class, () -> this.sourcePathProcessor.processInResourceReader(JAVA_SOURCE_RESOURCE_NAME, reader -> { throw new RuntimeException(); })); + + assertSame(empty(), this.sourcePathProcessor.processInResourceReader(JAVA_SOURCE_RESOURCE_NAME, reader -> { + throw new RuntimeException(); + }, (f, e) -> null)); } @Test @@ -128,6 +143,10 @@ void testProcessInResourceContentOnFailed() { assertThrows(RuntimeException.class, () -> this.sourcePathProcessor.processInResourceContent(JAVA_SOURCE_RESOURCE_NAME, content -> { throw new RuntimeException(); })); + + assertSame(empty(), this.sourcePathProcessor.processInResourceContent(JAVA_SOURCE_RESOURCE_NAME, content -> { + throw new RuntimeException(); + }, (f, e) -> null)); } @Test @@ -142,6 +161,12 @@ void testProcessInResourceOutputStreamOnFailed() { assertThrows(RuntimeException.class, () -> this.classOutputProcessor.processInResourceOutputStream(this.randomResourceName, outputStream -> { throw new RuntimeException(); })); + + this.classOutputProcessor.processInResourceOutputStream(this.randomResourceName, outputStream -> { + throw new RuntimeException(); + }, (f, e) -> { + assertNotNull(e); + }); } @Test @@ -156,6 +181,12 @@ void testProcessInResourceOnWriterOnFailed() { assertThrows(RuntimeException.class, () -> this.classOutputProcessor.processInResourceWriter(randomResourceName, writer -> { throw new RuntimeException(); })); + + this.classOutputProcessor.processInResourceWriter(this.randomResourceName, writer -> { + throw new RuntimeException(); + }, (f, e) -> { + assertNotNull(e); + }); } @Test From 0ea8bafd1f6bbe4e9dfd4fe40b2c374bbc5d1998 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 21:15:17 +0800 Subject: [PATCH 065/438] Add TestDeque and simplify AbstractDequeTest Introduce TestDeque (backed by LinkedList) as a reusable test implementation of AbstractDeque with configurable max capacity. Replace the large anonymous deque instance in AbstractDequeTest#setUp with TestDeque<>(1), remove now-unused imports, and update the testRemoveFirstOccurrence assertion to expect false when removing null. These changes improve test clarity and reuse. --- .../collection/AbstractDequeTest.java | 106 +------------ .../io/microsphere/collection/TestDeque.java | 144 ++++++++++++++++++ 2 files changed, 146 insertions(+), 104 deletions(-) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/collection/TestDeque.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/AbstractDequeTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/AbstractDequeTest.java index eb6aa11fe..df8696b1b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/AbstractDequeTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/AbstractDequeTest.java @@ -3,9 +3,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Iterator; import java.util.NoSuchElementException; -import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -28,107 +26,7 @@ class AbstractDequeTest { @BeforeEach void setUp() { - - deque = new AbstractDeque() { - - private String value; - - @Override - public Iterator iterator() { - return new Iterator() { - - private int cursor = 0; - - @Override - public boolean hasNext() { - return cursor == 0; - } - - @Override - public String next() { - if (cursor++ == 0) { - return value; - } else { - throw new NoSuchElementException(); - } - } - - @Override - public void remove() { - if (cursor <= 1) { - value = null; - } else { - throw new NoSuchElementException(); - } - } - }; - } - - @Override - public Iterator descendingIterator() { - return iterator(); - } - - @Override - public boolean offerFirst(String s) { - if (value == null) { - value = s; - return true; - } - return false; - } - - @Override - public boolean offerLast(String s) { - return offerFirst(s); - } - - @Override - public String pollFirst() { - String s = value; - value = null; - return s; - } - - @Override - public String pollLast() { - return pollFirst(); - } - - @Override - public String getFirst() { - return value; - } - - @Override - public String getLast() { - return getFirst(); - } - - @Override - public String peekFirst() { - return value; - } - - @Override - public String peekLast() { - return value; - } - - @Override - public boolean removeLastOccurrence(Object o) { - if (Objects.equals(o, value)) { - value = null; - return true; - } - return false; - } - - @Override - public int size() { - return 1; - } - }; + deque = new TestDeque<>(1); } @Test @@ -189,7 +87,7 @@ void testPeekLast() { @Test void testRemoveFirstOccurrence() { - assertTrue(deque.removeFirstOccurrence(null)); + assertFalse(deque.removeFirstOccurrence(null)); deque.add(TEST_VALUE); assertFalse(deque.removeFirstOccurrence("")); assertTrue(deque.removeFirstOccurrence(TEST_VALUE)); diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/TestDeque.java b/microsphere-java-core/src/test/java/io/microsphere/collection/TestDeque.java new file mode 100644 index 000000000..fc60e5f90 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/TestDeque.java @@ -0,0 +1,144 @@ +/* + * 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. + */ + +package io.microsphere.collection; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import static java.lang.Integer.MAX_VALUE; + +/** + * {@link AbstractDeque} class for testing + * + * @author Mercy + * @see AbstractDeque + * @since 1.0.0 + */ +public class TestDeque extends AbstractDeque { + + private final int maxCapacity; + + private final LinkedList delegate = new LinkedList<>(); + + public TestDeque() { + this(MAX_VALUE); + } + + public TestDeque(int maxCapacity) { + this.maxCapacity = maxCapacity; + } + + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + @Override + public void forEach(Consumer action) { + delegate.forEach(action); + } + + @Override + public boolean removeIf(Predicate filter) { + return delegate.removeIf(filter); + } + + @Override + public Spliterator spliterator() { + return delegate.spliterator(); + } + + @Override + public Stream stream() { + return delegate.stream(); + } + + @Override + public Stream parallelStream() { + return delegate.parallelStream(); + } + + @Override + public Iterator descendingIterator() { + return delegate.descendingIterator(); + } + + @Override + public boolean offerFirst(E e) { + if (isFull()) { + return false; + } + return delegate.offerFirst(e); + } + + @Override + public boolean offerLast(E e) { + if (isFull()) { + return false; + } + return delegate.offerLast(e); + } + + @Override + public E pollFirst() { + return delegate.pollFirst(); + } + + @Override + public E pollLast() { + return delegate.pollLast(); + } + + @Override + public E getFirst() { + return delegate.getFirst(); + } + + @Override + public E getLast() { + return delegate.getLast(); + } + + @Override + public E peekFirst() { + return delegate.peekFirst(); + } + + @Override + public E peekLast() { + return delegate.peekLast(); + } + + @Override + public boolean removeLastOccurrence(Object o) { + return delegate.removeLastOccurrence(o); + } + + @Override + public int size() { + return delegate.size(); + } + + public boolean isFull() { + return this.size() >= this.maxCapacity; + } +} \ No newline at end of file From 841f1dbe9656e8966548904e34563fb8a15fa607 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 21:18:54 +0800 Subject: [PATCH 066/438] Add explicit Deque method overrides in TestDeque Added explicit overrides for common Deque operations (addFirst, addLast, removeFirst, removeLast, removeFirstOccurrence, push, pop, offer, poll, peek) in TestDeque that simply delegate to the superclass. This clarifies the class API and ensures these methods are available/overridable for testing or further extension without changing behavior. --- .../io/microsphere/collection/TestDeque.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/TestDeque.java b/microsphere-java-core/src/test/java/io/microsphere/collection/TestDeque.java index fc60e5f90..f14674a7b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/TestDeque.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/TestDeque.java @@ -47,6 +47,56 @@ public TestDeque(int maxCapacity) { this.maxCapacity = maxCapacity; } + @Override + public void addFirst(E e) { + super.addFirst(e); + } + + @Override + public void addLast(E e) { + super.addLast(e); + } + + @Override + public E removeFirst() { + return super.removeFirst(); + } + + @Override + public E removeLast() { + return super.removeLast(); + } + + @Override + public boolean removeFirstOccurrence(Object o) { + return super.removeFirstOccurrence(o); + } + + @Override + public void push(E e) { + super.push(e); + } + + @Override + public E pop() { + return super.pop(); + } + + @Override + public boolean offer(E e) { + return super.offer(e); + } + + @Override + public E poll() { + return super.poll(); + } + + @Override + public E peek() { + return super.peek(); + } + @Override public Iterator iterator() { return delegate.iterator(); From fc44d09cb06d30479b2c4475568b6c960bcdc3ad Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 5 Feb 2026 21:42:52 +0800 Subject: [PATCH 067/438] Add DelegatingIterator and update EmptyIterator Introduce DelegatingIterator that forwards all Iterator calls to an underlying delegate and implements DelegatingWrapper to expose the delegate. Refactor EmptyIterator to extend DelegatingIterator and use Collections.emptyIterator() as its delegate. Add DelegatingIteratorTest to verify delegation behavior (hasNext, next, remove, forEachRemaining, getDelegate, hashCode, equals, toString). This enables easier iterator wrapping and reuse of common iterator behavior. --- .../collection/DelegatingIterator.java | 88 +++++++++++++++ .../microsphere/collection/EmptyIterator.java | 20 +--- .../collection/DelegatingIteratorTest.java | 100 ++++++++++++++++++ 3 files changed, 190 insertions(+), 18 deletions(-) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/collection/DelegatingIterator.java create mode 100644 microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingIteratorTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/DelegatingIterator.java b/microsphere-java-core/src/main/java/io/microsphere/collection/DelegatingIterator.java new file mode 100644 index 000000000..3975f19ab --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/DelegatingIterator.java @@ -0,0 +1,88 @@ +/* + * 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. + */ +package io.microsphere.collection; + +import io.microsphere.lang.DelegatingWrapper; + +import java.util.Iterator; +import java.util.function.Consumer; + +/** + * A delegating implementation of the {@link Iterator} interface that forwards all method calls to a delegate iterator. + * This class is useful when you want to wrap an existing iterator and potentially override some of its behavior. + * + *

    Example Usage

    + *
    {@code
    + * List list = Arrays.asList("a", "b", "c");
    + * Iterator iterator = new DelegatingIterator<>(list.iterator());
    + * while (iterator.hasNext()) {
    + *     System.out.println(iterator.next());
    + * }
    + * }
    + * + * @param the type of elements returned by this iterator + * @author Mercy + * @see Iterator + */ +public class DelegatingIterator implements Iterator, DelegatingWrapper { + + private final Iterator delegate; + + public DelegatingIterator(Iterator delegate) { + this.delegate = delegate; + } + + @Override + public final boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public final E next() { + return delegate.next(); + } + + @Override + public final void remove() { + delegate.remove(); + } + + @Override + public final void forEachRemaining(Consumer action) { + this.delegate.forEachRemaining(action); + } + + @Override + public final Object getDelegate() { + return this.delegate; + } + + @Override + public final int hashCode() { + return this.delegate.hashCode(); + } + + @Override + public final boolean equals(Object obj) { + return this.delegate.equals(obj); + } + + @Override + public final String toString() { + return this.delegate.toString(); + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/EmptyIterator.java b/microsphere-java-core/src/main/java/io/microsphere/collection/EmptyIterator.java index 83f95542d..08514248e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/collection/EmptyIterator.java +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/EmptyIterator.java @@ -55,31 +55,15 @@ * @see Collections#emptyIterator() */ @Immutable -public class EmptyIterator implements Iterator { +public class EmptyIterator extends DelegatingIterator { /** * The singleton of {@link EmptyIterator} */ public static final EmptyIterator INSTANCE = new EmptyIterator(); - private final Iterator delegate; public EmptyIterator() { - this.delegate = emptyIterator(); - } - - @Override - public boolean hasNext() { - return delegate.hasNext(); - } - - @Override - public E next() { - return delegate.next(); - } - - @Override - public void remove() { - delegate.remove(); + super(emptyIterator()); } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingIteratorTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingIteratorTest.java new file mode 100644 index 000000000..f29382d37 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingIteratorTest.java @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package io.microsphere.collection; + + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import static io.microsphere.collection.ListUtils.ofArrayList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link DelegatingIterator} Test + * + * @author Mercy + * @see DelegatingIterator + * @since 1.0.0 + */ +class DelegatingIteratorTest { + + private Iterator iterator; + + private DelegatingIterator delegatingIterator; + + @BeforeEach + void setUp() { + List list = ofArrayList("1", "2", "3"); + this.iterator = list.iterator(); + this.delegatingIterator = new DelegatingIterator<>(this.iterator); + } + + @Test + void testHasNext() { + assertTrue(delegatingIterator.hasNext()); + } + + @Test + void testNext() { + assertEquals("1", this.delegatingIterator.next()); + assertEquals("2", this.delegatingIterator.next()); + assertEquals("3", this.delegatingIterator.next()); + assertThrows(NoSuchElementException.class, this.delegatingIterator::next); + } + + @Test + void testRemove() { + while (this.delegatingIterator.hasNext()) { + this.delegatingIterator.next(); + this.delegatingIterator.remove(); + } + } + + @Test + void testForEachRemaining() { + this.delegatingIterator.forEachRemaining(Assertions::assertNotNull); + } + + @Test + void testGetDelegate() { + assertSame(this.delegatingIterator.getDelegate(), this.iterator); + } + + @Test + void testHashCode() { + assertEquals(this.delegatingIterator.hashCode(), this.iterator.hashCode()); + } + + @Test + void testEquals() { + assertEquals(this.delegatingIterator, this.iterator); + } + + @Test + void testToString() { + assertEquals(this.delegatingIterator.toString(), this.iterator.toString()); + } +} \ No newline at end of file From 65edb6b582fcc5d0ce62b5798841e78e6dc2eadd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 00:48:04 +0800 Subject: [PATCH 068/438] Refactor JSONTokener char/position handling Centralize and simplify character access and position checks in JSONTokener. Replaced repeated this.in.charAt/length checks with helper methods (hasNext, nextChar, currentChar, charAt) and refactored loops to use them. Also added static imports for Double.valueOf, Long.parseLong, Integer.MAX_VALUE and MIN_VALUE and updated numeric parsing to use those imports. Adjusted more() and next() to use the new helpers and cleaned up related parsing methods to improve readability and reduce duplication. --- .../java/io/microsphere/json/JSONTokener.java | 54 +++++++++++++------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/json/JSONTokener.java b/microsphere-java-core/src/main/java/io/microsphere/json/JSONTokener.java index ac6e4318a..f17066c58 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/json/JSONTokener.java +++ b/microsphere-java-core/src/main/java/io/microsphere/json/JSONTokener.java @@ -19,6 +19,10 @@ import static io.microsphere.json.JSONObject.NULL; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; +import static java.lang.Double.valueOf; +import static java.lang.Integer.MAX_VALUE; +import static java.lang.Integer.MIN_VALUE; +import static java.lang.Long.parseLong; /** * Parses a JSON (RFC 4627) encoded @@ -117,8 +121,8 @@ public Object nextValue() throws JSONException { } int nextCleanInternal() throws JSONException { - while (this.pos < this.in.length()) { - int c = this.in.charAt(this.pos++); + while (hasNext()) { + int c = nextChar(); switch (c) { case '\t': case ' ': @@ -131,7 +135,7 @@ int nextCleanInternal() throws JSONException { return c; } - char peek = this.in.charAt(this.pos); + char peek = currentChar(); switch (peek) { case '*': // skip a /* c-style comment */ @@ -175,8 +179,8 @@ int nextCleanInternal() throws JSONException { * terminated by "\r\n", the '\n' must be consumed as whitespace by the caller. */ void skipToEndOfLine() { - for (; this.pos < this.in.length(); this.pos++) { - char c = this.in.charAt(this.pos); + for (; hasNext(); this.pos++) { + char c = currentChar(); if (c == '\r' || c == '\n') { this.pos++; break; @@ -206,8 +210,8 @@ public String nextString(char quote) throws JSONException { /* the index of the first character not yet appended to the builder. */ int start = this.pos; - while (this.pos < this.in.length()) { - int c = this.in.charAt(this.pos++); + while (hasNext()) { + int c = nextChar(); if (c == quote) { if (builder == null) { // a new string avoids leaking memory @@ -244,7 +248,7 @@ public String nextString(char quote) throws JSONException { * @throws JSONException if processing of json failed */ char readEscapeCharacter() throws JSONException { - char escaped = this.in.charAt(this.pos++); + char escaped = nextChar(); switch (escaped) { case 'u': if (this.pos + 4 > this.in.length()) { @@ -309,8 +313,8 @@ public Object readLiteral() throws JSONException { base = 8; } try { - long longValue = Long.parseLong(number, base); - if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { + long longValue = parseLong(number, base); + if (longValue <= MAX_VALUE && longValue >= MIN_VALUE) { return (int) longValue; } else { return longValue; @@ -326,7 +330,7 @@ public Object readLiteral() throws JSONException { /* ...next try to parse as a floating point... */ try { - return Double.valueOf(literal); + return valueOf(literal); } catch (NumberFormatException ignored) { } @@ -343,8 +347,8 @@ public Object readLiteral() throws JSONException { */ String nextToInternal(String excluded) { int start = this.pos; - for (; this.pos < this.in.length(); this.pos++) { - char c = this.in.charAt(this.pos); + for (; hasNext(); this.pos++) { + char c = currentChar(); if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) { return this.in.substring(start, this.pos); } @@ -389,7 +393,7 @@ public JSONObject readObject() throws JSONException { if (separator != ':' && separator != '=') { throw syntaxError("Expected ':' after " + name); } - if (this.pos < this.in.length() && this.in.charAt(this.pos) == '>') { + if (hasNext() && currentChar() == '>') { this.pos++; } @@ -486,11 +490,15 @@ public String toString() { */ public boolean more() { - return this.pos < this.in.length(); + return hasNext(); } public char next() { - return this.pos < this.in.length() ? this.in.charAt(this.pos++) : '\0'; + return hasNext() ? nextChar() : '\0'; + } + + public boolean hasNext() { + return this.pos < this.in.length(); } public char next(char c) throws JSONException { @@ -547,6 +555,18 @@ public void back() { } } + char currentChar() { + return charAt(this.pos); + } + + char nextChar() { + return charAt(this.pos++); + } + + char charAt(int pos) { + return this.in.charAt(pos); + } + public static int dehexchar(char hex) { if (hex >= '0' && hex <= '9') { return hex - '0'; @@ -559,4 +579,4 @@ public static int dehexchar(char hex) { } } -} +} \ No newline at end of file From 23d34961a52176404ac31c98b9abe53bba5b6058 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 00:58:59 +0800 Subject: [PATCH 069/438] Use static Scope imports and isEmpty() Refactor JSONStringer to use static imports for Scope enum constants and introduce an isEmpty() helper that wraps stack.isEmpty(). Replace direct Scope.* and stack.isEmpty() usages across methods (array/endArray/object/endObject/open/peek/value/beforeKey/beforeValue) to improve readability and consistency without changing behavior. --- .../io/microsphere/json/JSONStringer.java | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/json/JSONStringer.java b/microsphere-java-core/src/main/java/io/microsphere/json/JSONStringer.java index 832787050..37c6dbd51 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/json/JSONStringer.java +++ b/microsphere-java-core/src/main/java/io/microsphere/json/JSONStringer.java @@ -21,6 +21,11 @@ import static io.microsphere.json.JSONObject.NULL; import static io.microsphere.json.JSONObject.numberToString; +import static io.microsphere.json.JSONStringer.Scope.DANGLING_KEY; +import static io.microsphere.json.JSONStringer.Scope.EMPTY_ARRAY; +import static io.microsphere.json.JSONStringer.Scope.EMPTY_OBJECT; +import static io.microsphere.json.JSONStringer.Scope.NONEMPTY_ARRAY; +import static io.microsphere.json.JSONStringer.Scope.NONEMPTY_OBJECT; import static io.microsphere.util.ClassUtils.getTypeName; import static java.util.Arrays.fill; @@ -145,7 +150,7 @@ public JSONStringer(int indentSpaces) { * @throws JSONException if processing of json failed */ public JSONStringer array() throws JSONException { - return open(Scope.EMPTY_ARRAY, "["); + return open(EMPTY_ARRAY, "["); } /** @@ -155,7 +160,7 @@ public JSONStringer array() throws JSONException { * @throws JSONException if processing of json failed */ public JSONStringer endArray() throws JSONException { - return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]"); + return close(EMPTY_ARRAY, NONEMPTY_ARRAY, "]"); } /** @@ -166,7 +171,7 @@ public JSONStringer endArray() throws JSONException { * @throws JSONException if processing of json failed */ public JSONStringer object() throws JSONException { - return open(Scope.EMPTY_OBJECT, "{"); + return open(EMPTY_OBJECT, "{"); } /** @@ -176,7 +181,7 @@ public JSONStringer object() throws JSONException { * @throws JSONException if processing of json failed */ public JSONStringer endObject() throws JSONException { - return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}"); + return close(EMPTY_OBJECT, NONEMPTY_OBJECT, "}"); } /** @@ -188,7 +193,7 @@ public JSONStringer endObject() throws JSONException { * @throws JSONException if processing of json failed */ JSONStringer open(Scope empty, String openBracket) throws JSONException { - if (this.stack.isEmpty() && this.out.length() > 0) { + if (isEmpty() && this.out.length() > 0) { throw new JSONException("Nesting problem: multiple top-level roots"); } beforeValue(); @@ -228,7 +233,7 @@ JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSON * @throws JSONException if processing of json failed */ private Scope peek() throws JSONException { - if (this.stack.isEmpty()) { + if (isEmpty()) { throw new JSONException("Nesting problem"); } return this.stack.get(this.stack.size() - 1); @@ -253,7 +258,7 @@ private void replaceTop(Scope topOfStack) { * @throws JSONException if processing of json failed */ public JSONStringer value(Object value) throws JSONException { - if (this.stack.isEmpty()) { + if (isEmpty()) { throw new JSONException("Nesting problem"); } @@ -288,7 +293,7 @@ public JSONStringer value(Object value) throws JSONException { * @throws JSONException if processing of json failed */ public JSONStringer value(boolean value) throws JSONException { - if (this.stack.isEmpty()) { + if (isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); @@ -305,7 +310,7 @@ public JSONStringer value(boolean value) throws JSONException { * @throws JSONException if processing of json failed */ public JSONStringer value(double value) throws JSONException { - if (this.stack.isEmpty()) { + if (isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); @@ -321,7 +326,7 @@ public JSONStringer value(double value) throws JSONException { * @throws JSONException if processing of json failed */ public JSONStringer value(long value) throws JSONException { - if (this.stack.isEmpty()) { + if (isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); @@ -414,13 +419,13 @@ public JSONStringer key(String name) throws JSONException { */ void beforeKey() throws JSONException { Scope context = peek(); - if (context == Scope.NONEMPTY_OBJECT) { // first in object + if (context == NONEMPTY_OBJECT) { // first in object this.out.append(','); - } else if (context != Scope.EMPTY_OBJECT) { // not in an object! + } else if (context != EMPTY_OBJECT) { // not in an object! throw new JSONException("Nesting problem"); } newline(); - replaceTop(Scope.DANGLING_KEY); + replaceTop(DANGLING_KEY); } /** @@ -431,20 +436,20 @@ void beforeKey() throws JSONException { * @throws JSONException if processing of json failed */ void beforeValue() throws JSONException { - if (this.stack.isEmpty()) { + if (isEmpty()) { return; } Scope context = peek(); - if (context == Scope.EMPTY_ARRAY) { // first in array - replaceTop(Scope.NONEMPTY_ARRAY); + if (context == EMPTY_ARRAY) { // first in array + replaceTop(NONEMPTY_ARRAY); newline(); - } else if (context == Scope.NONEMPTY_ARRAY) { // another in array + } else if (context == NONEMPTY_ARRAY) { // another in array this.out.append(','); newline(); - } else if (context == Scope.DANGLING_KEY) { // value for key + } else if (context == DANGLING_KEY) { // value for key this.out.append(this.indent == null ? ":" : ": "); - replaceTop(Scope.NONEMPTY_OBJECT); + replaceTop(NONEMPTY_OBJECT); } else if (context != Scope.NULL) { throw new JSONException("Nesting problem"); } @@ -466,4 +471,7 @@ public String toString() { return this.out.length() == 0 ? null : this.out.toString(); } + boolean isEmpty() { + return this.stack.isEmpty(); + } } From c3202f0e9b484ca70e61d78f14b5a4c3aef92094 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 01:24:06 +0800 Subject: [PATCH 070/438] Use cached length in single-char split loop Replace value.length() with the precomputed 'length' variable when delimiterLength == 0. This ensures the loop boundary is consistent with earlier computations and avoids repeated method calls when splitting the string into single-character substrings. --- .../src/main/java/io/microsphere/util/StringUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java index b8e92bf57..8ca872b66 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java @@ -173,7 +173,7 @@ public static String[] split(@Nullable String value, @Nullable String delimiter) List result = new ArrayList<>(); if (delimiterLength == 0) { - for (int i = 0; i < value.length(); i++) { + for (int i = 0; i < length; i++) { result.add(value.substring(i, i + 1)); } } else { From 56e5b856c3c6cb7a407e9c5ccdd422f01ad7ae58 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 01:58:07 +0800 Subject: [PATCH 071/438] Use isObjectType check in TypeUtils loop Replace the manual null and Object.class checks with a call to isObjectType(targetClass) in the loop that collects resolved type arguments. This centralizes the terminal-condition logic for traversing the class hierarchy, improving readability and consistency when resolving type arguments. --- .../src/main/java/io/microsphere/reflect/TypeUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java index e0b2a6e60..0792648e4 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java @@ -1192,7 +1192,7 @@ public static List resolveTypeArguments(Class targetClass) { return emptyList(); } List typeArguments = newLinkedList(); - while (targetClass != null && targetClass != Object.class) { + while (!isObjectType(targetClass)) { typeArguments.addAll(resolveTypeArguments(targetClass.getGenericSuperclass())); typeArguments.addAll(resolveTypeArguments(targetClass.getGenericInterfaces())); targetClass = targetClass.getSuperclass(); From 804fd25877ad8da5b1e8b92f7430f4889b0c243f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 02:14:03 +0800 Subject: [PATCH 072/438] Revert "Use isObjectType check in TypeUtils loop" This reverts commit 56e5b856c3c6cb7a407e9c5ccdd422f01ad7ae58. --- .../src/main/java/io/microsphere/reflect/TypeUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java index 0792648e4..e0b2a6e60 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java @@ -1192,7 +1192,7 @@ public static List resolveTypeArguments(Class targetClass) { return emptyList(); } List typeArguments = newLinkedList(); - while (!isObjectType(targetClass)) { + while (targetClass != null && targetClass != Object.class) { typeArguments.addAll(resolveTypeArguments(targetClass.getGenericSuperclass())); typeArguments.addAll(resolveTypeArguments(targetClass.getGenericInterfaces())); targetClass = targetClass.getSuperclass(); From 244a8138cb293ee0aaf11a7f3e0864d980a932be Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 02:17:02 +0800 Subject: [PATCH 073/438] Enable JUnit Jupiter parallel execution Add junit.jupiter.execution.parallel.enabled = true to src/test/resources/junit-platform.properties to enable parallel test execution for JUnit Jupiter. This update allows tests to run concurrently during the test phase. --- .../src/test/resources/junit-platform.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/resources/junit-platform.properties b/microsphere-java-core/src/test/resources/junit-platform.properties index 1cebb76d5..993f2b029 100644 --- a/microsphere-java-core/src/test/resources/junit-platform.properties +++ b/microsphere-java-core/src/test/resources/junit-platform.properties @@ -1 +1,3 @@ -junit.jupiter.extensions.autodetection.enabled = true \ No newline at end of file +junit.jupiter.extensions.autodetection.enabled = true + +junit.jupiter.execution.parallel.enabled = true \ No newline at end of file From e22218d0ce08bd41382d72395aac5b160a2794f5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 08:55:59 +0800 Subject: [PATCH 074/438] Format signatures; remove redundant null check Reformat long method signatures in TypeUtils.resolveTypeArgumentsMap overloads for improved readability by wrapping parameters across lines. Also simplify the loop in resolveTypeArguments by removing a redundant null check (targetClass is validated earlier), leaving behavior unchanged while making the code clearer. --- .../java/io/microsphere/reflect/TypeUtils.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java index e0b2a6e60..d105b4059 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/TypeUtils.java @@ -646,7 +646,9 @@ static List doResolveActualTypeArgumentsInFastPath(Type type) { } @Nonnull - private static Map resolveTypeArgumentsMap(Type type, List hierarchicalTypes, int hierarchicalTypesSize, Class baseClass, TypeVariable[] baseTypeParameters) { + private static Map resolveTypeArgumentsMap(Type type, List hierarchicalTypes, + int hierarchicalTypesSize, Class baseClass, + TypeVariable[] baseTypeParameters) { int size = hierarchicalTypesSize + 1; @@ -663,14 +665,18 @@ private static Map resolveTypeArgumentsMap(Type type, Lis return typeArgumentsMap; } - private static void resolveTypeArgumentsMap(Type type, List hierarchicalTypes, int index, int hierarchicalTypesSize, Map typeArgumentsMap, Class baseClass, TypeVariable[] baseTypeParameters) { + private static void resolveTypeArgumentsMap(Type type, List hierarchicalTypes, int index, + int hierarchicalTypesSize, Map typeArgumentsMap, + Class baseClass, TypeVariable[] baseTypeParameters) { ParameterizedType pType = asParameterizedType(type); if (pType != null) { resolveTypeArgumentsMap(pType, hierarchicalTypes, index, hierarchicalTypesSize, typeArgumentsMap, baseClass, baseTypeParameters); } } - private static void resolveTypeArgumentsMap(ParameterizedType type, List hierarchicalTypes, int index, int hierarchicalTypesSize, Map typeArgumentsMap, Class baseClass, TypeVariable[] baseTypeParameters) { + private static void resolveTypeArgumentsMap(ParameterizedType type, List hierarchicalTypes, int index, + int hierarchicalTypesSize, Map typeArgumentsMap, + Class baseClass, TypeVariable[] baseTypeParameters) { Class klass = asClass(type); int baseTypeArgumentsLength = baseTypeParameters.length; @@ -1192,7 +1198,7 @@ public static List resolveTypeArguments(Class targetClass) { return emptyList(); } List typeArguments = newLinkedList(); - while (targetClass != null && targetClass != Object.class) { + while (targetClass != Object.class) { typeArguments.addAll(resolveTypeArguments(targetClass.getGenericSuperclass())); typeArguments.addAll(resolveTypeArguments(targetClass.getGenericInterfaces())); targetClass = targetClass.getSuperclass(); From 95cef367eb78c9bb2e01578be7094944bca3d6c1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 08:57:13 +0800 Subject: [PATCH 075/438] Add private constructor to Assert utility Prevent instantiation of the Assert utility class by adding a private no-arg constructor. This enforces the class's static-only usage and communicates intent to future maintainers. --- .../src/main/java/io/microsphere/util/Assert.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/Assert.java b/microsphere-java-core/src/main/java/io/microsphere/util/Assert.java index 362d81bad..262f56136 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/Assert.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/Assert.java @@ -502,4 +502,7 @@ public static void assertFieldMatchType(Object object, String fieldName, Class messageSupplier) { return messageSupplier == null ? null : messageSupplier.get(); } -} + + private Assert() { + } +} \ No newline at end of file From 1ab16cc0f3f71231dd30ca011379c3d24ffa32dc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 08:57:45 +0800 Subject: [PATCH 076/438] Remove super() from StringUtils constructor Remove the redundant super() call from the private no-arg constructor in StringUtils. This is a clean-up change with no behavioral impact since Object's constructor is invoked implicitly. --- .../src/main/java/io/microsphere/util/StringUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java index 8ca872b66..f5e8da389 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java @@ -882,6 +882,5 @@ static String trimWhitespace(String str, boolean includeLeading, boolean include } private StringUtils() { - super(); } -} +} \ No newline at end of file From 00bf5ebdeb09147bf65a71d2d0efa203bcebd517 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 12:04:18 +0800 Subject: [PATCH 077/438] Refactor DelegatingURLConnectionTest to use this. Normalize test code by prefixing urlConnection references with this.urlConnection and consolidating static imports (copyToString, attach, TEST_CONSOLE_URL, System.out, assertSame). Replace IOUtils.toString with copyToString, adjust testGetOutputStream to expect IOException, and add a scenario that attaches the ServiceLoaderURLStreamHandlerFactory, opens TEST_CONSOLE_URL and asserts the returned output stream is System.out. Miscellaneous small assertions and cleanup for clarity. --- .../net/DelegatingURLConnectionTest.java | 110 ++++++++++-------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/DelegatingURLConnectionTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/DelegatingURLConnectionTest.java index 7cfa84144..b2554b3ed 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/DelegatingURLConnectionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/DelegatingURLConnectionTest.java @@ -16,7 +16,6 @@ */ package io.microsphere.net; -import io.microsphere.io.IOUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -27,11 +26,16 @@ import java.util.List; import java.util.Map; +import static io.microsphere.io.IOUtils.copyToString; +import static io.microsphere.net.ServiceLoaderURLStreamHandlerFactory.attach; +import static io.microsphere.net.console.HandlerTest.TEST_CONSOLE_URL; import static java.lang.System.currentTimeMillis; +import static java.lang.System.out; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -96,207 +100,213 @@ void testGetReadTimeout() { @Test void testGetURL() { - assertEquals(url, urlConnection.getURL()); + assertEquals(url, this.urlConnection.getURL()); } @Test void testGetContentLength() { - assertEquals(19, urlConnection.getContentLength()); + assertEquals(19, this.urlConnection.getContentLength()); } @Test void testGetContentLengthLong() { - assertEquals(19, urlConnection.getContentLengthLong()); + assertEquals(19, this.urlConnection.getContentLengthLong()); } @Test void testGetContentType() { - assertEquals("content/unknown", urlConnection.getContentType()); + assertEquals("content/unknown", this.urlConnection.getContentType()); } @Test void testGetContentEncoding() { - assertNull(urlConnection.getContentEncoding()); + assertNull(this.urlConnection.getContentEncoding()); } @Test void testGetExpiration() { - assertEquals(0, urlConnection.getExpiration()); + assertEquals(0, this.urlConnection.getExpiration()); } @Test void testGetDate() { - assertEquals(0, urlConnection.getDate()); + assertEquals(0, this.urlConnection.getDate()); } @Test void testGetLastModified() { - assertFalse(urlConnection.getLastModified() > currentTimeMillis()); + assertFalse(this.urlConnection.getLastModified() > currentTimeMillis()); } @Test void testGetHeaderField() { - assertNull(urlConnection.getHeaderField(NOT_EXISTS_HEADER_NAME)); - assertEquals("19", urlConnection.getHeaderField(CONTENT_LENGTH_HEADER_NAME)); - assertNotNull(urlConnection.getHeaderField(LAST_MODIFIED_HEADER_NAME)); + assertNull(this.urlConnection.getHeaderField(NOT_EXISTS_HEADER_NAME)); + assertEquals("19", this.urlConnection.getHeaderField(CONTENT_LENGTH_HEADER_NAME)); + assertNotNull(this.urlConnection.getHeaderField(LAST_MODIFIED_HEADER_NAME)); } @Test void testGetHeaderFields() { - assertNotNull(urlConnection.getHeaderFields()); + assertNotNull(this.urlConnection.getHeaderFields()); } @Test void testGetHeaderFieldInt() { - assertEquals(19, urlConnection.getHeaderFieldInt(CONTENT_LENGTH_HEADER_NAME, 10)); - assertEquals(1, urlConnection.getHeaderFieldInt(NOT_EXISTS_HEADER_NAME, 1)); + assertEquals(19, this.urlConnection.getHeaderFieldInt(CONTENT_LENGTH_HEADER_NAME, 10)); + assertEquals(1, this.urlConnection.getHeaderFieldInt(NOT_EXISTS_HEADER_NAME, 1)); } @Test void testGetHeaderFieldLong() { - assertEquals(19, urlConnection.getHeaderFieldLong(CONTENT_LENGTH_HEADER_NAME, 10)); - assertEquals(1, urlConnection.getHeaderFieldLong(NOT_EXISTS_HEADER_NAME, 1)); + assertEquals(19, this.urlConnection.getHeaderFieldLong(CONTENT_LENGTH_HEADER_NAME, 10)); + assertEquals(1, this.urlConnection.getHeaderFieldLong(NOT_EXISTS_HEADER_NAME, 1)); } @Test void testGetHeaderFieldDate() { long now = currentTimeMillis(); - assertTrue(now > urlConnection.getHeaderFieldDate(LAST_MODIFIED_HEADER_NAME, now)); - assertEquals(now, urlConnection.getHeaderFieldDate(NOT_EXISTS_HEADER_NAME, now)); + assertTrue(now > this.urlConnection.getHeaderFieldDate(LAST_MODIFIED_HEADER_NAME, now)); + assertEquals(now, this.urlConnection.getHeaderFieldDate(NOT_EXISTS_HEADER_NAME, now)); } @Test void testGetHeaderFieldKey() { - assertEquals(CONTENT_LENGTH_HEADER_NAME, urlConnection.getHeaderFieldKey(0)); - assertEquals(LAST_MODIFIED_HEADER_NAME, urlConnection.getHeaderFieldKey(1)); - assertNull(urlConnection.getHeaderFieldKey(2)); + assertEquals(CONTENT_LENGTH_HEADER_NAME, this.urlConnection.getHeaderFieldKey(0)); + assertEquals(LAST_MODIFIED_HEADER_NAME, this.urlConnection.getHeaderFieldKey(1)); + assertNull(this.urlConnection.getHeaderFieldKey(2)); } @Test void testGetHeaderFieldWithInt() { - assertEquals("19", urlConnection.getHeaderField(0)); - assertNotNull(urlConnection.getHeaderField(1)); - assertNull(urlConnection.getHeaderFieldKey(2)); + assertEquals("19", this.urlConnection.getHeaderField(0)); + assertNotNull(this.urlConnection.getHeaderField(1)); + assertNull(this.urlConnection.getHeaderFieldKey(2)); } @Test void testGetContent() throws IOException { - urlConnection.getContent(); + this.urlConnection.getContent(); } @Test void testGetContentWithClassArray() throws IOException { - urlConnection.getContent(new Class[0]); + this.urlConnection.getContent(new Class[0]); } @Test void testGetPermission() throws IOException { - assertNotNull(urlConnection.getPermission()); + assertNotNull(this.urlConnection.getPermission()); } @Test void testGetInputStream() throws Exception { String encoding = "UTF-8"; String data = "name = 测试名称"; - assertEquals(data, IOUtils.toString(urlConnection.getInputStream(), encoding)); + assertEquals(data, copyToString(this.urlConnection.getInputStream(), encoding)); } @Test - void testGetOutputStream() throws Exception { - assertThrows(Exception.class, urlConnection::getOutputStream); + void testGetOutputStream() throws IOException { + assertThrows(Exception.class, this.urlConnection::getOutputStream); + + attach(); + URL url = new URL(TEST_CONSOLE_URL); + URLConnection delegate = url.openConnection(); + this.urlConnection = new DelegatingURLConnection(delegate); + assertSame(out, this.urlConnection.getOutputStream()); } @Test void testToString() { - assertNotNull(urlConnection.toString()); + assertNotNull(this.urlConnection.toString()); } @Test void testSetDoInput() { - urlConnection.setDoInput(true); + this.urlConnection.setDoInput(true); } @Test void testGetDoInput() { testSetDoInput(); - assertTrue(urlConnection.getDoInput()); + assertTrue(this.urlConnection.getDoInput()); } @Test void testSetDoOutput() { - urlConnection.setDoOutput(true); + this.urlConnection.setDoOutput(true); } @Test void testGetDoOutput() { testSetDoOutput(); - assertTrue(urlConnection.getDoOutput()); + assertTrue(this.urlConnection.getDoOutput()); } @Test void testSetAllowUserInteraction() { - urlConnection.setAllowUserInteraction(true); + this.urlConnection.setAllowUserInteraction(true); } @Test void testGetAllowUserInteraction() { testSetAllowUserInteraction(); - assertTrue(urlConnection.getAllowUserInteraction()); + assertTrue(this.urlConnection.getAllowUserInteraction()); } @Test void testSetUseCaches() { - urlConnection.setUseCaches(true); + this.urlConnection.setUseCaches(true); } @Test void testGetUseCaches() { testSetUseCaches(); - assertTrue(urlConnection.getUseCaches()); + assertTrue(this.urlConnection.getUseCaches()); } @Test void testSetIfModifiedSince() { long now = currentTimeMillis(); - urlConnection.setIfModifiedSince(now); + this.urlConnection.setIfModifiedSince(now); } @Test void testGetIfModifiedSince() { testSetIfModifiedSince(); - assertTrue(currentTimeMillis() >= urlConnection.getIfModifiedSince()); + assertTrue(currentTimeMillis() >= this.urlConnection.getIfModifiedSince()); } @Test void testSetDefaultUseCaches() { - urlConnection.setDefaultUseCaches(true); + this.urlConnection.setDefaultUseCaches(true); } @Test void testGetDefaultUseCaches() { - urlConnection.setDefaultUseCaches(true); - assertTrue(urlConnection.getDefaultUseCaches()); + this.urlConnection.setDefaultUseCaches(true); + assertTrue(this.urlConnection.getDefaultUseCaches()); } @Test void testSetRequestProperty() { - urlConnection.setRequestProperty("key-1", "value-1"); + this.urlConnection.setRequestProperty("key-1", "value-1"); } @Test void testAddRequestProperty() { - urlConnection.addRequestProperty("key-1", "value-1-1"); - urlConnection.addRequestProperty("key-2", "value-2"); + this.urlConnection.addRequestProperty("key-1", "value-1-1"); + this.urlConnection.addRequestProperty("key-2", "value-2"); } @Test void testGetRequestProperty() { - assertNull(urlConnection.getRequestProperty("key-1")); + assertNull(this.urlConnection.getRequestProperty("key-1")); } @Test void testGetRequestProperties() { - Map> requestProperties = urlConnection.getRequestProperties(); + Map> requestProperties = this.urlConnection.getRequestProperties(); assertNotNull(requestProperties); } } From 739be6556555c5a5071a6c2f9bcefe766973870d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 12:12:40 +0800 Subject: [PATCH 078/438] Use this.jsonBuilder in tests; add appendName test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor JSONUtilsTest to use explicit this.jsonBuilder references (replacing bare jsonBuilder) to avoid ambiguity and make the instance usage clear. Add static import for appendName and introduce testAppendName to validate appendName behavior for null, empty, blank and normal names. No production code changed—only test code and imports updated. --- .../io/microsphere/json/JSONUtilsTest.java | 181 ++++++++++-------- 1 file changed, 98 insertions(+), 83 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/json/JSONUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/json/JSONUtilsTest.java index 76420059c..d50fa6507 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/json/JSONUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/json/JSONUtilsTest.java @@ -46,6 +46,7 @@ import static io.microsphere.collection.Sets.ofSet; import static io.microsphere.json.JSONObject.NULL; import static io.microsphere.json.JSONUtils.append; +import static io.microsphere.json.JSONUtils.appendName; import static io.microsphere.json.JSONUtils.convertValue; import static io.microsphere.json.JSONUtils.determineElementClass; import static io.microsphere.json.JSONUtils.escape; @@ -99,219 +100,219 @@ void setUp() { @Test void testAppendOnBoolean() { - append(jsonBuilder, "name", true); - assertEquals("\"name\":true", jsonBuilder.toString()); + append(this.jsonBuilder, "name", true); + assertEquals("\"name\":true", this.jsonBuilder.toString()); } @Test void testAppendOnByte() { - append(jsonBuilder, "name", (byte) 1); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", (byte) 1); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnShort() { - append(jsonBuilder, "name", (short) 1); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", (short) 1); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnInt() { - append(jsonBuilder, "name", 1); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", 1); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnLong() { - append(jsonBuilder, "name", 1L); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", 1L); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnFloat() { - append(jsonBuilder, "name", 1.0f); - assertEquals("\"name\":1.0", jsonBuilder.toString()); + append(this.jsonBuilder, "name", 1.0f); + assertEquals("\"name\":1.0", this.jsonBuilder.toString()); } @Test void testAppendOnDouble() { - append(jsonBuilder, "name", 1.0); - assertEquals("\"name\":1.0", jsonBuilder.toString()); + append(this.jsonBuilder, "name", 1.0); + assertEquals("\"name\":1.0", this.jsonBuilder.toString()); } @Test void testAppendOnChar() { - append(jsonBuilder, "name", 'a'); - assertEquals("\"name\":\"a\"", jsonBuilder.toString()); + append(this.jsonBuilder, "name", 'a'); + assertEquals("\"name\":\"a\"", this.jsonBuilder.toString()); } @Test void testAppendOnBooleanObject() { - append(jsonBuilder, "name", TRUE); - assertEquals("\"name\":true", jsonBuilder.toString()); + append(this.jsonBuilder, "name", TRUE); + assertEquals("\"name\":true", this.jsonBuilder.toString()); } @Test void testAppendOnByteObject() { - append(jsonBuilder, "name", valueOf((byte) 1)); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", valueOf((byte) 1)); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnShortObject() { - append(jsonBuilder, "name", Short.valueOf((short) 1)); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", Short.valueOf((short) 1)); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnIntegerObject() { - append(jsonBuilder, "name", Integer.valueOf(1)); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", Integer.valueOf(1)); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnLongObject() { - append(jsonBuilder, "name", Long.valueOf(1L)); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", Long.valueOf(1L)); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnFloatObject() { - append(jsonBuilder, "name", Float.valueOf(1.0f)); - assertEquals("\"name\":1.0", jsonBuilder.toString()); + append(this.jsonBuilder, "name", Float.valueOf(1.0f)); + assertEquals("\"name\":1.0", this.jsonBuilder.toString()); } @Test void testAppendOnDoubleObject() { - append(jsonBuilder, "name", Double.valueOf(1.0)); - assertEquals("\"name\":1.0", jsonBuilder.toString()); + append(this.jsonBuilder, "name", Double.valueOf(1.0)); + assertEquals("\"name\":1.0", this.jsonBuilder.toString()); } @Test void testAppendOnCharacterObject() { - append(jsonBuilder, "name", valueOf('a')); - assertEquals("\"name\":\"a\"", jsonBuilder.toString()); + append(this.jsonBuilder, "name", valueOf('a')); + assertEquals("\"name\":\"a\"", this.jsonBuilder.toString()); } @Test void testAppendOnString() { - append(jsonBuilder, "name", "a"); - assertEquals("\"name\":\"a\"", jsonBuilder.toString()); + append(this.jsonBuilder, "name", "a"); + assertEquals("\"name\":\"a\"", this.jsonBuilder.toString()); } @Test void testAppendOnType() { - append(jsonBuilder, "name", String.class); - assertEquals("\"name\":\"java.lang.String\"", jsonBuilder.toString()); + append(this.jsonBuilder, "name", String.class); + assertEquals("\"name\":\"java.lang.String\"", this.jsonBuilder.toString()); } @Test void testAppendOnBooleanObjectArray() { - append(jsonBuilder, "name", new Boolean[]{TRUE, FALSE}); - assertEquals("\"name\":[true,false]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new Boolean[]{TRUE, FALSE}); + assertEquals("\"name\":[true,false]", this.jsonBuilder.toString()); } @Test void testAppendOnByteObjectArray() { - append(jsonBuilder, "name", new Byte[]{(byte) 1, (byte) 2}); - assertEquals("\"name\":[1,2]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new Byte[]{(byte) 1, (byte) 2}); + assertEquals("\"name\":[1,2]", this.jsonBuilder.toString()); } @Test void testAppendOnShortObjectArray() { - append(jsonBuilder, "name", new Short[]{(short) 1, (short) 2}); - assertEquals("\"name\":[1,2]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new Short[]{(short) 1, (short) 2}); + assertEquals("\"name\":[1,2]", this.jsonBuilder.toString()); } @Test void testAppendOnIntegerObjectArray() { - append(jsonBuilder, "name", new Integer[]{1, 2}); - assertEquals("\"name\":[1,2]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new Integer[]{1, 2}); + assertEquals("\"name\":[1,2]", this.jsonBuilder.toString()); } @Test void testAppendOnLongObjectArray() { - append(jsonBuilder, "name", new Long[]{1L, 2L}); - assertEquals("\"name\":[1,2]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new Long[]{1L, 2L}); + assertEquals("\"name\":[1,2]", this.jsonBuilder.toString()); } @Test void testAppendOnFloatObjectArray() { - append(jsonBuilder, "name", new Float[]{1.0f, 2.0f}); - assertEquals("\"name\":[1.0,2.0]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new Float[]{1.0f, 2.0f}); + assertEquals("\"name\":[1.0,2.0]", this.jsonBuilder.toString()); } @Test void testAppendOnDoubleObjectArray() { - append(jsonBuilder, "name", new Double[]{1.0, 2.0}); - assertEquals("\"name\":[1.0,2.0]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new Double[]{1.0, 2.0}); + assertEquals("\"name\":[1.0,2.0]", this.jsonBuilder.toString()); } @Test void testAppendOnCharacterObjectArray() { - append(jsonBuilder, "name", new Character[]{'a', 'b'}); - assertEquals("\"name\":[\"a\",\"b\"]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new Character[]{'a', 'b'}); + assertEquals("\"name\":[\"a\",\"b\"]", this.jsonBuilder.toString()); } @Test void testAppendOnBooleanArray() { - append(jsonBuilder, "name", new boolean[]{true, false}); - assertEquals("\"name\":[true,false]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new boolean[]{true, false}); + assertEquals("\"name\":[true,false]", this.jsonBuilder.toString()); } @Test void testAppendOnByteArray() { - append(jsonBuilder, "name", new byte[]{1, 2}); - assertEquals("\"name\":[1,2]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new byte[]{1, 2}); + assertEquals("\"name\":[1,2]", this.jsonBuilder.toString()); } @Test void testAppendOnShortArray() { - append(jsonBuilder, "name", new short[]{1, 2}); - assertEquals("\"name\":[1,2]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new short[]{1, 2}); + assertEquals("\"name\":[1,2]", this.jsonBuilder.toString()); } @Test void testAppendOnIntegerArray() { - append(jsonBuilder, "name", new int[]{1, 2}); - assertEquals("\"name\":[1,2]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new int[]{1, 2}); + assertEquals("\"name\":[1,2]", this.jsonBuilder.toString()); } @Test void testAppendOnLongArray() { - append(jsonBuilder, "name", new long[]{1, 2}); - assertEquals("\"name\":[1,2]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new long[]{1, 2}); + assertEquals("\"name\":[1,2]", this.jsonBuilder.toString()); } @Test void testAppendOnFloatArray() { - append(jsonBuilder, "name", new float[]{1.0f, 2.0f}); - assertEquals("\"name\":[1.0,2.0]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new float[]{1.0f, 2.0f}); + assertEquals("\"name\":[1.0,2.0]", this.jsonBuilder.toString()); } @Test void testAppendOnDoubleArray() { - append(jsonBuilder, "name", new double[]{1.0, 2.0}); - assertEquals("\"name\":[1.0,2.0]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new double[]{1.0, 2.0}); + assertEquals("\"name\":[1.0,2.0]", this.jsonBuilder.toString()); } @Test void testAppendOnCharArray() { - append(jsonBuilder, "name", new char[]{'a', 'b'}); - assertEquals("\"name\":[\"a\",\"b\"]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new char[]{'a', 'b'}); + assertEquals("\"name\":[\"a\",\"b\"]", this.jsonBuilder.toString()); } @Test void testAppendOnStringArray() { - append(jsonBuilder, "name", new String[]{"a", "b"}); - assertEquals("\"name\":[\"a\",\"b\"]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", new String[]{"a", "b"}); + assertEquals("\"name\":[\"a\",\"b\"]", this.jsonBuilder.toString()); } @Test void testAppendOnObjectAsArray() { Object value = new boolean[]{TRUE, FALSE}; - append(jsonBuilder, "name", value); - assertEquals("\"name\":[true,false]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", value); + assertEquals("\"name\":[true,false]", this.jsonBuilder.toString()); } @Test @@ -328,44 +329,58 @@ void testAppendOnObjectAsMap() { "string", "8", "strings", new String[]{"9", "10"} ); - append(jsonBuilder, "name", value); + append(this.jsonBuilder, "name", value); } @Test void testAppendOnObjectAsIterable() { Object value = ofList(true, (byte) 1, '2', 3.0, 4.0f, 5L, 6, (short) 7, "8", ofArray("9", "10")); - append(jsonBuilder, "name", value); - assertEquals("\"name\":[true,1,\"2\",3.0,4.0,5,6,7,\"8\",[\"9\",\"10\"]]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", value); + assertEquals("\"name\":[true,1,\"2\",3.0,4.0,5,6,7,\"8\",[\"9\",\"10\"]]", this.jsonBuilder.toString()); } @Test void testAppendOnObjectAsString() { Object value = "s"; - append(jsonBuilder, "name", value); - assertEquals("\"name\":\"s\"", jsonBuilder.toString()); + append(this.jsonBuilder, "name", value); + assertEquals("\"name\":\"s\"", this.jsonBuilder.toString()); } @Test void testAppendOnObjectAsInteger() { Object value = 1; - append(jsonBuilder, "name", value); - assertEquals("\"name\":1", jsonBuilder.toString()); + append(this.jsonBuilder, "name", value); + assertEquals("\"name\":1", this.jsonBuilder.toString()); } @Test void testAppendOnGenericArray() { TimeUnit[] values = ofArray(DAYS, HOURS, MINUTES); - append(jsonBuilder, "name", values); - assertEquals("\"name\":[\"DAYS\",\"HOURS\",\"MINUTES\"]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", values); + assertEquals("\"name\":[\"DAYS\",\"HOURS\",\"MINUTES\"]", this.jsonBuilder.toString()); } @Test void testAppendOnTypeArray() { Class[] classes = ofArray(String.class, Integer.class); - append(jsonBuilder, "name", classes); - assertEquals("\"name\":[\"java.lang.String\",\"java.lang.Integer\"]", jsonBuilder.toString()); + append(this.jsonBuilder, "name", classes); + assertEquals("\"name\":[\"java.lang.String\",\"java.lang.Integer\"]", this.jsonBuilder.toString()); } + @Test + void testAppendName() { + appendName(this.jsonBuilder, null); + assertEquals(EMPTY_STRING, this.jsonBuilder.toString()); + + appendName(this.jsonBuilder, ""); + assertEquals(EMPTY_STRING, this.jsonBuilder.toString()); + + appendName(this.jsonBuilder, " "); + assertEquals(EMPTY_STRING, this.jsonBuilder.toString()); + + appendName(this.jsonBuilder, "name"); + assertEquals("\"name\":", this.jsonBuilder.toString()); + } @Test void testIsUnknownClass() { From 101a86d44fc0907c8d10964a1be7ebce7f90b8f6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 15:26:39 +0800 Subject: [PATCH 079/438] Simplify process id resolution with streams Replace the manual loop for resolving current process id with a Stream-based pipeline that filters supported ProcessIdResolvers, maps to their current id, and picks the first non-null value or UNKNOWN_PROCESS_ID. Remove the Logger, the log(...) helper and related imports since trace logging was eliminated. Update tests to reflect the change: remove the logging-centric test and unused imports, and add an assertion that the static currentProcessId equals the value returned by getCurrentProcessId(). Miscellaneous: add Objects import and tidy up unused imports. --- .../management/ManagementUtils.java | 33 +++++-------------- .../management/ManagementUtilsTest.java | 22 ++++--------- 2 files changed, 15 insertions(+), 40 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/ManagementUtils.java b/microsphere-java-core/src/main/java/io/microsphere/management/ManagementUtils.java index b14fa3f49..e96d8e662 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/ManagementUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/ManagementUtils.java @@ -1,14 +1,12 @@ package io.microsphere.management; -import io.microsphere.logging.Logger; import io.microsphere.process.ProcessIdResolver; import io.microsphere.util.ServiceLoaderUtils; import io.microsphere.util.Utils; -import java.util.List; +import java.util.Objects; -import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.process.ProcessIdResolver.UNKNOWN_PROCESS_ID; import static io.microsphere.util.ServiceLoaderUtils.loadServicesList; @@ -22,29 +20,16 @@ */ public abstract class ManagementUtils implements Utils { - private static final Logger logger = getLogger(ManagementUtils.class); - static final long currentProcessId = resolveCurrentProcessId(); private static long resolveCurrentProcessId() { - List resolvers = loadServicesList(ProcessIdResolver.class); - Long processId = null; - for (ProcessIdResolver resolver : resolvers) { - if (resolver.supports()) { - if ((processId = resolver.current()) != null) { - log(resolver, processId); - break; - } - } - } - return processId == null ? UNKNOWN_PROCESS_ID : processId; - } - - static void log(ProcessIdResolver resolver, Long processId) { - if (logger.isTraceEnabled()) { - logger.trace("The process id was resolved by ProcessIdResolver[class : '{}' , priority : {}] successfully : {}", - resolver.getClass().getName(), resolver.getPriority(), processId); - } + return loadServicesList(ProcessIdResolver.class) + .stream() + .filter(ProcessIdResolver::supports) + .map(ProcessIdResolver::current) + .filter(Objects::nonNull) + .findFirst() + .orElse(UNKNOWN_PROCESS_ID); } /** @@ -75,4 +60,4 @@ public static long getCurrentProcessId() { private ManagementUtils() { } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/ManagementUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/ManagementUtilsTest.java index 7f682bc44..bb40748ba 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/ManagementUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/ManagementUtilsTest.java @@ -1,14 +1,11 @@ package io.microsphere.management; import io.microsphere.AbstractTestCase; -import io.microsphere.process.ProcessIdResolver; import org.junit.jupiter.api.Test; -import java.util.List; - +import static io.microsphere.management.ManagementUtils.currentProcessId; import static io.microsphere.management.ManagementUtils.getCurrentProcessId; -import static io.microsphere.process.ProcessIdResolver.UNKNOWN_PROCESS_ID; -import static io.microsphere.util.ServiceLoaderUtils.loadServicesList; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -22,16 +19,9 @@ class ManagementUtilsTest extends AbstractTestCase { @Test void testGetCurrentProcessId() { - long currentProcessId = getCurrentProcessId(); - assertTrue(currentProcessId > 0); - } - - @Test - void testLog() { - List resolvers = loadServicesList(ProcessIdResolver.class); - for (ProcessIdResolver resolver : resolvers) { - ManagementUtils.log(resolver, UNKNOWN_PROCESS_ID); - } + long processId = getCurrentProcessId(); + assertTrue(processId > 0); + assertEquals(currentProcessId, getCurrentProcessId()); } -} +} \ No newline at end of file From f7235b1317776484c85b108fecbc143caa993c1f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 16:25:27 +0800 Subject: [PATCH 080/438] Make load methods static and add tests Refactor BannedArtifactClassLoadingExecutor to make config-loading helpers static and accept a ClassLoader (loadBannedArtifactConfigs(ClassLoader), loadBannedArtifactConfigs(URL), loadBannedArtifactConfig(String)). execute() now passes this.classLoader and uses a stream forEach for artifact processing. loadBannedArtifactConfig now throws IllegalArgumentException for invalid definitions. Tests added to cover execution and error cases (failed resource lookup and invalid definition). Also removed an unused test field as part of cleanup. --- .../BannedArtifactClassLoadingExecutor.java | 31 ++++++++----------- ...annedArtifactClassLoadingExecutorTest.java | 30 ++++++++++++++++-- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutor.java index 98749c614..9c2375975 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutor.java @@ -81,21 +81,20 @@ public BannedArtifactClassLoadingExecutor(@Nullable ClassLoader classLoader) { } public void execute() { - List bannedArtifactConfigs = loadBannedArtifactConfigs(); + List bannedArtifactConfigs = loadBannedArtifactConfigs(this.classLoader); List artifacts = artifactDetector.detect(false); - for (Artifact artifact : artifacts) { - URL classPathURL = artifact.getLocation(); - if (classPathURL != null) { - for (MavenArtifact bannedArtifactConfig : bannedArtifactConfigs) { - if (bannedArtifactConfig.matches(artifact)) { - removeClassPathURL(classLoader, classPathURL); + artifacts.stream() + .forEach(artifact -> { + URL classPathURL = artifact.getLocation(); + for (MavenArtifact bannedArtifactConfig : bannedArtifactConfigs) { + if (bannedArtifactConfig.matches(artifact)) { + removeClassPathURL(classLoader, classPathURL); + } } - } - } - } + }); } - private List loadBannedArtifactConfigs() { + static List loadBannedArtifactConfigs(ClassLoader classLoader) { List bannedArtifactConfigs = new LinkedList<>(); try { Enumeration configResources = classLoader.getResources(CONFIG_LOCATION); @@ -110,7 +109,7 @@ private List loadBannedArtifactConfigs() { return bannedArtifactConfigs; } - private List loadBannedArtifactConfigs(URL configResource) throws IOException { + static List loadBannedArtifactConfigs(URL configResource) throws IOException { List bannedArtifactConfigs = new LinkedList<>(); try (InputStream inputStream = configResource.openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, ENCODING)) @@ -127,14 +126,10 @@ private List loadBannedArtifactConfigs(URL configResource) throws return bannedArtifactConfigs; } - /** - * @param definition - * @return - */ - private MavenArtifact loadBannedArtifactConfig(String definition) { + static MavenArtifact loadBannedArtifactConfig(String definition) throws IllegalArgumentException { String[] gav = split(definition.trim(), COLON); if (gav.length != 3) { - throw new RuntimeException("The definition of the banned artifact must contain groupId, artifactId and version : " + definition); + throw new IllegalArgumentException("The definition of the banned artifact must contain groupId, artifactId and version : " + definition); } String groupId = gav[0]; String artifactId = gav[1]; diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutorTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutorTest.java index f5e15dff7..f94a96960 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutorTest.java @@ -18,6 +18,15 @@ import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import static io.microsphere.classloading.BannedArtifactClassLoadingExecutor.loadBannedArtifactConfig; +import static io.microsphere.classloading.BannedArtifactClassLoadingExecutor.loadBannedArtifactConfigs; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * {@link BannedArtifactClassLoadingExecutor} Test * @@ -26,10 +35,25 @@ */ class BannedArtifactClassLoadingExecutorTest { - private BannedArtifactClassLoadingExecutor detector = new BannedArtifactClassLoadingExecutor(); - @Test void testDetect() { + BannedArtifactClassLoadingExecutor detector = new BannedArtifactClassLoadingExecutor(); detector.execute(); } -} + + @Test + void testLoadBannedArtifactConfigsOnFailed() { + ClassLoader classLoader = new ClassLoader() { + @Override + public Enumeration getResources(String name) throws IOException { + throw new IOException("For testing"); + } + }; + assertTrue(loadBannedArtifactConfigs(classLoader).isEmpty()); + } + + @Test + void testLoadBannedArtifactConfigOnFailed() { + assertThrows(IllegalArgumentException.class, () -> loadBannedArtifactConfig("invalid-definition")); + } +} \ No newline at end of file From 5465a7628bbd5f2f872642e60f6dcaf5308fdf01 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 16:36:55 +0800 Subject: [PATCH 081/438] Disable Surefire system classloader in CI Add -Dsurefire.useSystemClassLoader=false to the Maven test invocation in the GitHub Actions workflow (.github/workflows/maven-build.yml) to avoid Surefire classloader-related issues during test execution and improve test reliability in CI. --- .github/workflows/maven-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index b2267b040..0bbaba8d5 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -36,6 +36,7 @@ jobs: --update-snapshots --file pom.xml -Drevision=0.0.1-SNAPSHOT + -Dsurefire.useSystemClassLoader=false test --activate-profiles test,coverage From 973f253f76009ec72303c38b6fc03617647401d4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 16:46:47 +0800 Subject: [PATCH 082/438] Use collection forEach instead of stream().forEach Replace artifacts.stream().forEach(...) with artifacts.forEach(...) to avoid unnecessary stream creation and simplify the iteration. This is a small stylistic cleanup with no functional change. --- .../BannedArtifactClassLoadingExecutor.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutor.java index 9c2375975..4d5f8325a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/BannedArtifactClassLoadingExecutor.java @@ -83,15 +83,14 @@ public BannedArtifactClassLoadingExecutor(@Nullable ClassLoader classLoader) { public void execute() { List bannedArtifactConfigs = loadBannedArtifactConfigs(this.classLoader); List artifacts = artifactDetector.detect(false); - artifacts.stream() - .forEach(artifact -> { - URL classPathURL = artifact.getLocation(); - for (MavenArtifact bannedArtifactConfig : bannedArtifactConfigs) { - if (bannedArtifactConfig.matches(artifact)) { - removeClassPathURL(classLoader, classPathURL); - } - } - }); + artifacts.forEach(artifact -> { + URL classPathURL = artifact.getLocation(); + for (MavenArtifact bannedArtifactConfig : bannedArtifactConfigs) { + if (bannedArtifactConfig.matches(artifact)) { + removeClassPathURL(classLoader, classPathURL); + } + } + }); } static List loadBannedArtifactConfigs(ClassLoader classLoader) { From b573b7fef330b05db9a84e3faad70555662a7629 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 20:16:36 +0800 Subject: [PATCH 083/438] Refactor ReflectionUtils and update tests Refactor reflection utilities to use common helpers and improve null/edge-case handling. Replaced direct Thread.currentThread() calls with a static import, unified field access via FieldUtils.getFieldValue, and switched static checks to MemberUtils.isStatic. Simplified toObject() and readFieldsAsMap() to handle nulls, recursive structures and return empty/unmodifiable maps; use newLinkedHashMap for initial sizing. Introduced overloads for isInaccessibleObjectException to accept Class and String types and added Javadoc. Updated unit tests to cover toObject, readFieldsAsMap recursion, and the new isInaccessibleObjectException behavior, plus minor import and assertion adjustments. --- .../microsphere/reflect/ReflectionUtils.java | 94 ++++++++++++------- .../reflect/ReflectionUtilsTest.java | 42 ++++++++- 2 files changed, 101 insertions(+), 35 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java index f92cd2481..c675db165 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java @@ -12,18 +12,21 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; +import static io.microsphere.collection.MapUtils.newLinkedHashMap; import static io.microsphere.logging.LoggerFactory.getLogger; -import static io.microsphere.reflect.AccessibleObjectUtils.trySetAccessible; +import static io.microsphere.reflect.FieldUtils.getFieldValue; +import static io.microsphere.reflect.MemberUtils.isStatic; +import static io.microsphere.reflect.TypeUtils.getTypeName; import static io.microsphere.util.ClassLoaderUtils.resolveClass; +import static io.microsphere.util.ClassUtils.getType; import static io.microsphere.util.ClassUtils.isPrimitive; import static io.microsphere.util.ClassUtils.isSimpleType; import static java.lang.Class.forName; -import static java.lang.reflect.Modifier.isStatic; +import static java.lang.Thread.currentThread; +import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; /** @@ -128,7 +131,7 @@ public abstract class ReflectionUtils implements Utils { static { int invocationFrame = 0; // Use java.lang.StackTraceElement to calculate frame - StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + StackTraceElement[] stackTraceElements = currentThread().getStackTrace(); for (StackTraceElement stackTraceElement : stackTraceElements) { String className = stackTraceElement.getClassName(); if (TYPE.getName().equals(className)) { @@ -217,7 +220,7 @@ static String getCallerClassNameInGeneralJVM() { * @return specified invocation frame class */ static String getCallerClassNameInGeneralJVM(int invocationFrame) throws IndexOutOfBoundsException { - StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + StackTraceElement[] elements = currentThread().getStackTrace(); if (invocationFrame < elements.length) { StackTraceElement targetStackTraceElement = elements[invocationFrame]; return targetStackTraceElement.getClassName(); @@ -387,16 +390,14 @@ public static List toList(Object array) throws IllegalArgumentException { return list; } - private static Object toObject(Object object) { - if (object == null) { - return object; - } - Class type = object.getClass(); - if (type.isArray()) { - return toList(object); - } else { - return object; + static Object toObject(Object object) { + if (object != null) { + Class type = object.getClass(); + if (type.isArray()) { + return toList(object); + } } + return object; } @@ -445,31 +446,27 @@ private static Object toObject(Object object) { @Nonnull @Immutable public static Map readFieldsAsMap(Object object) { - Map fieldsAsMap = new LinkedHashMap(); + if (object == null) { + return emptyMap(); + } Class type = object.getClass(); Field[] fields = type.getDeclaredFields(); + Map fieldsAsMap = newLinkedHashMap(fields.length); for (Field field : fields) { - if (isStatic(field.getModifiers())) { // To filter static fields + if (isStatic(field)) { // To filter static fields continue; } - trySetAccessible(field); - - try { - String fieldName = field.getName(); - Object fieldValue = field.get(object); - if (fieldValue != null && fieldValue != object) { - Class fieldValueType = fieldValue.getClass(); - if (!isPrimitive(fieldValueType) - && !isSimpleType(fieldValueType) - && !Objects.equals(object.getClass(), fieldValueType)) { - fieldValue = readFieldsAsMap(fieldValue); - } - fieldsAsMap.put(fieldName, fieldValue); + String fieldName = field.getName(); + Object fieldValue = getFieldValue(object, field); + Class fieldValueType = field.getType(); + if (fieldValue != object) { + if (!isPrimitive(fieldValueType) && !isSimpleType(fieldValueType) + && !object.getClass().equals(fieldValueType)) { + fieldValue = readFieldsAsMap(fieldValue); } - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); + fieldsAsMap.put(fieldName, fieldValue); } } return unmodifiableMap(fieldsAsMap); @@ -502,10 +499,39 @@ public static Map readFieldsAsMap(Object object) { * {@link java.lang.reflect.InaccessibleObjectException}, false otherwise. */ public static boolean isInaccessibleObjectException(Throwable failure) { - return failure != null && INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME.equals(failure.getClass().getName()); + return isInaccessibleObjectException(getType(failure)); } - private ReflectionUtils() { + /** + * Checks whether the specified {@link Class} represents + * {@link java.lang.reflect.InaccessibleObjectException}. + * + *

    This method is useful when dealing with reflection operations that may fail due to module system + * restrictions introduced in JDK 9+. It avoids direct dependency on the presence of the class, which + * may not be available in earlier JDK versions.

    + * + *

    Example Usage

    + *
    {@code
    +     * Class exceptionClass = SomeException.class;
    +     * if (ReflectionUtils.isInaccessibleObjectException(exceptionClass)) {
    +     *     System.err.println("The class represents InaccessibleObjectException");
    +     * } else {
    +     *     System.out.println("The class does not represent InaccessibleObjectException");
    +     * }
    +     * }
    + * + * @param throwableClass The {@link Class} to check. + * @return true if the specified {@link Class} represents + * {@link java.lang.reflect.InaccessibleObjectException}, false otherwise. + */ + public static boolean isInaccessibleObjectException(Class throwableClass) { + return isInaccessibleObjectException(getTypeName(throwableClass)); } + static boolean isInaccessibleObjectException(String className) { + return INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME.equals(className); + } + + private ReflectionUtils() { + } } \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java index 9087a231b..6d8c42b7b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java @@ -1,24 +1,33 @@ package io.microsphere.reflect; import io.microsphere.AbstractTestCase; +import io.microsphere.test.Data; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.List; import java.util.Map; +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.reflect.ReflectionUtils.INACCESSIBLE_OBJECT_EXCEPTION_CLASS; +import static io.microsphere.reflect.ReflectionUtils.INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME; import static io.microsphere.reflect.ReflectionUtils.getCallerClass; import static io.microsphere.reflect.ReflectionUtils.getCallerClassInGeneralJVM; import static io.microsphere.reflect.ReflectionUtils.getCallerClassInSunJVM; import static io.microsphere.reflect.ReflectionUtils.getCallerClassName; import static io.microsphere.reflect.ReflectionUtils.getCallerClassNameInGeneralJVM; import static io.microsphere.reflect.ReflectionUtils.getCallerClassNameInSunJVM; +import static io.microsphere.reflect.ReflectionUtils.isInaccessibleObjectException; import static io.microsphere.reflect.ReflectionUtils.isSupportedSunReflectReflection; import static io.microsphere.reflect.ReflectionUtils.readFieldsAsMap; import static io.microsphere.reflect.ReflectionUtils.toList; +import static io.microsphere.reflect.ReflectionUtils.toObject; +import static io.microsphere.util.ArrayUtils.ofArray; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ReflectionUtils} Test @@ -30,7 +39,7 @@ class ReflectionUtilsTest extends AbstractTestCase { @Test - void testGetCallerClassX() throws Exception { + void testGetCallerClassX() { Class expectedClass = ReflectionUtilsTest.class; Class callerClass = getCallerClass(); @@ -90,5 +99,36 @@ void testReadFieldsAsMap() { value.put("c", "c"); map = readFieldsAsMap(value); assertFalse(map.isEmpty()); + + T t = new T(); + t.self = t; + t.other = new T(); + + map = readFieldsAsMap(t); + assertFalse(map.isEmpty()); + } + + @Test + void testToObject() { + assertNull(toObject(null)); + assertEquals("test", toObject("test")); + assertEquals(ofList("a", "b", "c"), toObject(ofArray("a", "b", "c"))); + } + + @Test + void testIsInaccessibleObjectException() { + assertFalse(isInaccessibleObjectException(new RuntimeException())); + assertFalse(isInaccessibleObjectException((Throwable) null)); + assertFalse(isInaccessibleObjectException((Class) null)); + assertFalse(isInaccessibleObjectException(Class.class)); + assertEquals(INACCESSIBLE_OBJECT_EXCEPTION_CLASS != null, isInaccessibleObjectException(INACCESSIBLE_OBJECT_EXCEPTION_CLASS)); + assertTrue(isInaccessibleObjectException(INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME)); + } + + static class T extends Data { + + private T self; + + private T other; } } From f612511d7ebd66a7d5cd2c66e3a71d982001e60b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 20:49:47 +0800 Subject: [PATCH 084/438] Invoke shutdown hook and adjust ExecutorUtilsTest Modify ExecutorUtilsTest to explicitly qualify executorService with this and to simulate JVM shutdown by fetching ShutdownHookUtils.shutdownHookCallbacks (via getStaticFieldValue), polling the registered callback and running it, then asserting the executor is shutdown. Added necessary imports (ShutdownHookUtils, PriorityBlockingQueue, FieldUtils). Minor EOF newline change in ExecutorUtils.java. --- .../microsphere/concurrent/ExecutorUtils.java | 2 +- .../concurrent/ExecutorUtilsTest.java | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java b/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java index a4cfc45e8..c3b97c056 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java @@ -136,4 +136,4 @@ public static boolean shutdown(ExecutorService executorService) { private ExecutorUtils() { } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java index ec6866136..255295db8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java @@ -1,15 +1,18 @@ package io.microsphere.concurrent; import io.microsphere.AbstractTestCase; +import io.microsphere.util.ShutdownHookUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; +import java.util.concurrent.PriorityBlockingQueue; import static io.microsphere.concurrent.CustomizedThreadFactory.newThreadFactory; import static io.microsphere.concurrent.ExecutorUtils.shutdown; import static io.microsphere.concurrent.ExecutorUtils.shutdownOnExit; +import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -27,19 +30,23 @@ class ExecutorUtilsTest extends AbstractTestCase { @BeforeEach void setUp() { - executorService = newSingleThreadExecutor(newThreadFactory("ExecutorUtilsTest-", true)); - executorService.execute(() -> log("Running...")); + this.executorService = newSingleThreadExecutor(newThreadFactory("ExecutorUtilsTest-", true)); + this.executorService.execute(() -> log("Running...")); } @Test void testShutdownOnExit() { - shutdownOnExit(executorService, executorService, executorService); + shutdownOnExit(this.executorService, this.executorService, this.executorService); + PriorityBlockingQueue shutdownHookCallbacks = getStaticFieldValue(ShutdownHookUtils.class, "shutdownHookCallbacks"); + Runnable callback = shutdownHookCallbacks.poll(); + callback.run(); + assertTrue(this.executorService.isShutdown()); } @Test void testShutdownForExecutor() { - assertTrue(shutdown((Executor) executorService)); - assertTrue(shutdown((Executor) executorService)); + assertTrue(shutdown((Executor) this.executorService)); + assertTrue(shutdown((Executor) this.executorService)); } @Test @@ -49,8 +56,8 @@ void testShutdownForExecutorOnNull() { @Test void testShutdownForExecutorService() { - assertTrue(shutdown(executorService)); - assertTrue(shutdown(executorService)); + assertTrue(shutdown(this.executorService)); + assertTrue(shutdown(this.executorService)); } @Test From 1de621c74f8ca49d92754bb29ac8da03913e62fa Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 21:27:25 +0800 Subject: [PATCH 085/438] Log ExecutorService shutdown and harden tests Add tracing log when an ExecutorService is shut down by introducing a Logger into ExecutorUtils. This helps debugging executor lifecycle events. Update ExecutorUtilsTest to be more robust: add @AfterEach tearDown to shut down the executor, make testShutdownOnExit handle InterruptedException, iterate over ShutdownHookUtils' callbacks to find and run the lambda-based shutdown callbacks (instead of assuming queue order), remove executed callbacks, and wait briefly for termination before asserting shutdown. Also add necessary imports and assertions. --- .../microsphere/concurrent/ExecutorUtils.java | 5 ++++ .../concurrent/ExecutorUtilsTest.java | 30 ++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java b/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java index c3b97c056..930ce67d8 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java @@ -16,6 +16,7 @@ */ package io.microsphere.concurrent; +import io.microsphere.logging.Logger; import io.microsphere.util.ShutdownHookUtils; import io.microsphere.util.Utils; @@ -23,6 +24,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.ArrayUtils.forEach; import static io.microsphere.util.ShutdownHookUtils.addShutdownHookCallback; @@ -37,6 +39,8 @@ */ public abstract class ExecutorUtils implements Utils { + private static final Logger logger = getLogger(ExecutorUtils.class); + /** * Registers a shutdown hook to gracefully shut down the given {@link Executor} instances when the JVM exits. * @@ -131,6 +135,7 @@ public static boolean shutdown(ExecutorService executorService) { if (!executorService.isShutdown()) { executorService.shutdown(); } + logger.trace("The ExecutorService({}) has been shutdown", executorService); return true; } diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java index 255295db8..26d821203 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java @@ -2,9 +2,11 @@ import io.microsphere.AbstractTestCase; import io.microsphere.util.ShutdownHookUtils; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.Iterator; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.PriorityBlockingQueue; @@ -14,7 +16,9 @@ import static io.microsphere.concurrent.ExecutorUtils.shutdownOnExit; import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -34,13 +38,31 @@ void setUp() { this.executorService.execute(() -> log("Running...")); } + @AfterEach + void tearDown() { + this.executorService.shutdown(); + } + @Test - void testShutdownOnExit() { + void testShutdownOnExit() throws InterruptedException { shutdownOnExit(this.executorService, this.executorService, this.executorService); PriorityBlockingQueue shutdownHookCallbacks = getStaticFieldValue(ShutdownHookUtils.class, "shutdownHookCallbacks"); - Runnable callback = shutdownHookCallbacks.poll(); - callback.run(); - assertTrue(this.executorService.isShutdown()); + assertNotNull(shutdownHookCallbacks); + + Iterator iterator = shutdownHookCallbacks.iterator(); + String classNamePrefix = ExecutorUtils.class.getName() + "$$Lambda/"; + while (iterator.hasNext()) { + Runnable callback = iterator.next(); + Class callbackClass = callback.getClass(); + String callbackClassName = callbackClass.getName(); + if (callbackClassName.startsWith(classNamePrefix)) { + callback.run(); + iterator.remove(); + } + } + if (this.executorService.awaitTermination(50, MILLISECONDS)) { + assertTrue(this.executorService.isShutdown()); + } } @Test From 3da036e1442302c6c5d51f1cc68c55208d09a5a6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 21:34:21 +0800 Subject: [PATCH 086/438] Call super in priority and add equals test In AbstractConverterTest, the anonymous converter now calls super.resolvePriority() and overrides getPriority() to return super.getPriority(). Also adds testEquals() to assert the converter equals itself. These changes improve test coverage for priority delegation and equality behavior. --- .../io/microsphere/convert/AbstractConverterTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java index b8a2273d3..5148e64ad 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java @@ -44,8 +44,14 @@ protected Object doConvert(Object source) throws Throwable { @Override protected Integer resolvePriority() { + super.resolvePriority(); return null; } + + @Override + public int getPriority() { + return super.getPriority(); + } }; } @@ -76,4 +82,8 @@ void testGetConverter() { assertNotEquals(this.converter, new Object()); } + @Test + void testEquals() { + assertEquals(this.converter, this.converter); + } } From ab9143f32310fc176b84e6f4b4ed06936b39c4d7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 21:37:55 +0800 Subject: [PATCH 087/438] Use Objects.nonNull and simplify trace logging Replace explicit null comparison with java.util.Objects.nonNull for readability in supports(). Remove the manual isTraceEnabled() guard and call logger.trace directly in current(), relying on the logger implementation to check the level. No functional behavior changes; just small cleanup and simplification. --- .../io/microsphere/process/ModernProcessIdResolver.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ModernProcessIdResolver.java b/microsphere-java-core/src/main/java/io/microsphere/process/ModernProcessIdResolver.java index 6a138378d..c1c606418 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ModernProcessIdResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ModernProcessIdResolver.java @@ -22,6 +22,7 @@ import static io.microsphere.reflect.MethodUtils.invokeMethod; import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; import static io.microsphere.util.ClassLoaderUtils.resolveClass; +import static java.util.Objects.nonNull; /** * A {@link ProcessIdResolver} implementation for modern JDKs (Java 9+). @@ -56,16 +57,14 @@ public class ModernProcessIdResolver implements ProcessIdResolver { @Override public boolean supports() { - return PROCESS_HANDLE_CLASS != null; + return nonNull(PROCESS_HANDLE_CLASS); } @Override public Long current() { Object processHandle = invokeStaticMethod(PROCESS_HANDLE_CLASS, "current"); Long pid = invokeMethod(processHandle, PROCESS_HANDLE_CLASS, "pid"); - if (logger.isTraceEnabled()) { - logger.trace("The PID was resolved from the method 'java.lang.ProcessHandle#pid()' : {}", pid); - } + logger.trace("The PID was resolved from the method 'java.lang.ProcessHandle#pid()' : {}", pid); return pid; } From a58eae282ecb96a0566ded26de1df2077590516c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 21:42:16 +0800 Subject: [PATCH 088/438] Use Objects.nonNull and simplify trace logging Replace explicit null check with java.util.Objects.nonNull for JVM_FIELD and remove the manual logger.isTraceEnabled() guard, calling logger.trace(...) directly. This simplifies the code and improves readability without changing behavior. --- .../process/VirtualMachineProcessIdResolver.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java b/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java index ddfa97746..a2e48bbba 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java @@ -27,6 +27,7 @@ import static io.microsphere.reflect.FieldUtils.getFieldValue; import static io.microsphere.reflect.MethodUtils.invokeMethod; import static java.lang.Long.valueOf; +import static java.util.Objects.nonNull; /** * A {@link ProcessIdResolver} implementation for retrieving the process ID using the SUN JVM internal APIs. @@ -81,7 +82,7 @@ public class VirtualMachineProcessIdResolver implements ProcessIdResolver { @Override public boolean supports() { - return JVM_FIELD != null; + return nonNull(JVM_FIELD); } @Override @@ -89,9 +90,7 @@ public Long current() { RuntimeMXBean runtimeMXBean = getRuntimeMXBean(); Object jvm = getFieldValue(runtimeMXBean, JVM_FIELD); Integer processId = invokeMethod(jvm, GET_PROCESS_ID_METHOD_NAME); - if (logger.isTraceEnabled()) { - logger.trace("The PID was resolved from the native method 'sun.management.VMManagementImpl#getProcessId()' : {}", processId); - } + logger.trace("The PID was resolved from the native method 'sun.management.VMManagementImpl#getProcessId()' : {}", processId); return valueOf(processId.longValue()); } From 2aac5117592c564b66f1e0e92954a4b2141e1c54 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 6 Feb 2026 21:43:02 +0800 Subject: [PATCH 089/438] Remove isTraceEnabled guard in PID resolver Simplify ClassicProcessIdResolver#current() by removing the explicit logger.isTraceEnabled() check and calling logger.trace(...) directly to log the resolved runtimeName and processId. Also remove the trailing newline at EOF in the file. --- .../io/microsphere/process/ClassicProcessIdResolver.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ClassicProcessIdResolver.java b/microsphere-java-core/src/main/java/io/microsphere/process/ClassicProcessIdResolver.java index 7a1ce136c..10ff9f6ac 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ClassicProcessIdResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ClassicProcessIdResolver.java @@ -82,9 +82,7 @@ public boolean supports() { @Override public Long current() { Long processId = valueOf(processIdValue); - if (logger.isTraceEnabled()) { - logger.trace("The PID was resolved from the method 'java.lang.management.RuntimeMXBean#getName()' = {} : {}", runtimeName, processId); - } + logger.trace("The PID was resolved from the method 'java.lang.management.RuntimeMXBean#getName()' = {} : {}", runtimeName, processId); return processId; } @@ -92,4 +90,4 @@ public Long current() { public int getPriority() { return NORMAL_PRIORITY + 9; } -} +} \ No newline at end of file From af148ad36eb990dd8f789512b0eb2f1b508a9628 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 02:46:15 +0800 Subject: [PATCH 090/438] Handle read edge cases; update tests Fix FastByteArrayInputStream.read(byte[],int,int) to correctly handle len==0 (return 0), detect EOF when no bytes are available (return -1), and clamp len to available bytes before copying. Update unit tests to cover reading with requested length larger than available, zero-length reads, EOF reads, skip edge cases (negative and very large skips), and equals edge cases (null, different type, self). --- .../microsphere/io/FastByteArrayInputStream.java | 13 ++++++++----- .../io/FastByteArrayInputStreamTest.java | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayInputStream.java b/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayInputStream.java index a297812dc..46a799ce2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayInputStream.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayInputStream.java @@ -110,17 +110,20 @@ public int read(byte[] b, int off, int len) { throw new IndexOutOfBoundsException(); } - if (pos >= count) { - return -1; + if (len == 0) { + return 0; } int avail = count - pos; + + if (avail < 1) { + return -1; + } + if (len > avail) { len = avail; } - if (len <= 0) { - return 0; - } + arraycopy(buf, pos, b, off, len); pos += len; return len; diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FastByteArrayInputStreamTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FastByteArrayInputStreamTest.java index 9d598ffce..6e10eae5b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FastByteArrayInputStreamTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FastByteArrayInputStreamTest.java @@ -9,6 +9,7 @@ import static java.lang.Integer.MAX_VALUE; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; /** @@ -58,7 +59,7 @@ void testRead() { } @Test - void testRead1() { + void testReadWithRange() { byte[] bytes = new byte[8]; int offset = 0; int length = inputStream.available(); @@ -66,8 +67,12 @@ void testRead1() { assertEquals(TEST_VALUE, new String(bytes, offset, length)); length = TEST_VALUE.length() - TEST_OFFSET; - assertEquals(length, inputStream2.read(bytes, offset, length)); + assertEquals(length, inputStream2.read(bytes, offset, length * 2)); assertEquals("llo", new String(bytes, offset, length)); + + assertEquals(0, inputStream2.read(bytes, offset, 0)); + + assertEquals(-1, inputStream2.read(bytes, offset, length)); } @Test @@ -92,8 +97,10 @@ void testRead1OnIndexOutOfBounds() { @Test void testSkip() { + assertEquals(0, inputStream.skip(-1)); assertEquals(0, inputStream.skip(0)); assertEquals(1, inputStream.skip(1)); + assertEquals(4, inputStream.skip(MAX_VALUE)); } @Test @@ -120,7 +127,10 @@ void testReset() { @Test void testEquals() { + assertFalse(inputStream.equals(null)); + assertFalse(inputStream.equals("test")); assertEquals(inputStream, inputStream2); + assertEquals(inputStream, inputStream); } @Test From 04e9fc375e9f94ed656920aff3fe736790216297 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 02:59:49 +0800 Subject: [PATCH 091/438] Refactor service loader and update tests Simplify ServiceLoadingURLClassPathHandle by using a Stream to select the first supporting URLClassPathHandle (filter -> findFirst -> orElse(null)) and use Objects.nonNull in supports() for clarity. Update unit tests: remove unused imports, split and rename tests to cover supports(), getURLs() and removeURL(), add assertions for null-classloader returning EMPTY_URL_ARRAY, and use this.handle consistently. These changes modernize the implementation and improve test coverage and readability. --- .../ServiceLoadingURLClassPathHandle.java | 14 ++++++------- .../ServiceLoadingURLClassPathHandleTest.java | 21 ++++++++++++++----- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/ServiceLoadingURLClassPathHandle.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/ServiceLoadingURLClassPathHandle.java index 658f679f0..5feb33760 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/ServiceLoadingURLClassPathHandle.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/ServiceLoadingURLClassPathHandle.java @@ -21,6 +21,7 @@ import static io.microsphere.util.ArrayUtils.isEmpty; import static io.microsphere.util.ServiceLoaderUtils.loadServicesList; +import static java.util.Objects.nonNull; /** * {@link URLClassPathHandle} implementation based on the Service Loading mechanism @@ -34,17 +35,16 @@ public class ServiceLoadingURLClassPathHandle implements URLClassPathHandle { public ServiceLoadingURLClassPathHandle() { List urlClassPathHandles = loadServicesList(URLClassPathHandle.class); - for (URLClassPathHandle urlClassPathHandle : urlClassPathHandles) { - if (urlClassPathHandle.supports()) { - this.delegate = urlClassPathHandle; - break; - } - } + this.delegate = urlClassPathHandles + .stream() + .filter(URLClassPathHandle::supports) + .findFirst() + .orElse(null); } @Override public boolean supports() { - return delegate != null; + return nonNull(this.delegate); } @Override diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/ServiceLoadingURLClassPathHandleTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/ServiceLoadingURLClassPathHandleTest.java index 09261ba82..82a41f39e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/ServiceLoadingURLClassPathHandleTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/ServiceLoadingURLClassPathHandleTest.java @@ -19,12 +19,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import static io.microsphere.net.URLUtils.EMPTY_URL_ARRAY; import static io.microsphere.util.ClassLoaderUtils.getDefaultClassLoader; import static io.microsphere.util.ClassLoaderUtils.newURLClassLoader; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -44,16 +45,26 @@ void setUp() { } @Test - void test() throws IOException { - ServiceLoadingURLClassPathHandle handle = new ServiceLoadingURLClassPathHandle(); - assertTrue(handle.supports()); + void testSupports() { + assertTrue(this.handle.supports()); + } + + @Test + void testGetURLs() { ClassLoader classLoader = getDefaultClassLoader(); URL[] urls = handle.getURLs(classLoader); assertNotNull(urls); + assertArrayEquals(EMPTY_URL_ARRAY, handle.getURLs(null)); + } + + @Test + void testRemoveURL() { + ClassLoader classLoader = getDefaultClassLoader(); + URL[] urls = this.handle.getURLs(classLoader); URLClassLoader urlClassLoader = newURLClassLoader(urls); for (URL url : urls) { - assertTrue(handle.removeURL(urlClassLoader, url)); + assertTrue(this.handle.removeURL(urlClassLoader, url)); } } } From aacbf0d116cb0fed571d6c837d52f155da46ebff Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 03:47:40 +0800 Subject: [PATCH 092/438] Add ClassUtils code-source helpers and tests Introduce utility methods in ClassUtils: getProtectionDomain, getCodeSource and getCodeSourceLocation to centralize obtaining a class's ProtectionDomain/CodeSource/URL. Replace in-place ProtectionDomain/CodeSource handling in ClassPathUtils with getCodeSourceLocation to simplify logic and remove redundant imports. Update tests: ClassUtilsTest adds unit tests for the new methods; ClassPathUtilsTest imports the ClassDataRepository singleton, adds a synthetic-type check and minor formatting tweaks. Misc: adjusted imports and removed unused security imports in ClassPathUtils. --- .../io/microsphere/util/ClassPathUtils.java | 13 +-- .../java/io/microsphere/util/ClassUtils.java | 90 +++++++++++++++++++ .../microsphere/util/ClassPathUtilsTest.java | 15 +++- .../io/microsphere/util/ClassUtilsTest.java | 25 ++++++ 4 files changed, 129 insertions(+), 14 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java index debf3035d..a3a1c8784 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java @@ -10,8 +10,6 @@ import java.lang.management.RuntimeMXBean; import java.net.URL; -import java.security.CodeSource; -import java.security.ProtectionDomain; import java.util.Set; import static io.microsphere.collection.SetUtils.ofSet; @@ -21,6 +19,7 @@ import static io.microsphere.util.ClassLoaderUtils.getDefaultClassLoader; import static io.microsphere.util.ClassLoaderUtils.isLoadedClass; import static io.microsphere.util.ClassLoaderUtils.resolveClass; +import static io.microsphere.util.ClassUtils.getCodeSourceLocation; import static io.microsphere.util.StringUtils.split; import static java.lang.ClassLoader.getSystemClassLoader; import static java.util.Collections.emptySet; @@ -189,13 +188,7 @@ public static URL getRuntimeClassLocation(Class type) { ClassLoader classLoader = type.getClassLoader(); URL location = null; if (classLoader != null) { // Non-Bootstrap - try { - ProtectionDomain protectionDomain = type.getProtectionDomain(); - CodeSource codeSource = protectionDomain.getCodeSource(); - location = codeSource == null ? null : codeSource.getLocation(); - } catch (SecurityException exception) { - - } + location = getCodeSourceLocation(type); } else if (!type.isPrimitive() && !type.isArray() && !type.isSynthetic()) { // Bootstrap ClassLoader // Class was loaded by Bootstrap ClassLoader location = getClassResource(getSystemClassLoader(), type.getName()); @@ -206,4 +199,4 @@ public static URL getRuntimeClassLocation(Class type) { private ClassPathUtils() { } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java index 35a2beb27..45f2e42e5 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java @@ -18,6 +18,9 @@ import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; import java.util.Date; import java.util.LinkedHashSet; import java.util.List; @@ -2126,6 +2129,93 @@ public static T cast(@Nullable Object object, @Nullable Class castType) { return castType.isInstance(object) ? castType.cast(object) : null; } + /** + * Gets the protection domain of the specified class. + *

    + * This method returns the {@link ProtectionDomain} of the given class. + * The protection domain encapsulates the security information associated + * with the class, such as the code source and permissions. If the class + * is {@code null}, this method returns {@code null}. + *

    + * + *

    Example Usage

    + *
    {@code
    +     * ProtectionDomain pd = ClassUtils.getProtectionDomain(String.class);
    +     * // Returns the protection domain of the String class
    +     *
    +     * ProtectionDomain nullPd = ClassUtils.getProtectionDomain(null);
    +     * // Returns null since the input class is null
    +     * }
    + * + * @param type the class to get the protection domain from, may be {@code null} + * @return the protection domain of the specified class, or {@code null} if the class is {@code null} + * @see Class#getProtectionDomain() + */ + @Nullable + public static ProtectionDomain getProtectionDomain(@Nullable Class type) { + return type == null ? null : type.getProtectionDomain(); + } + + /** + * Gets the code source of the specified class. + *

    + * This method returns the {@link CodeSource} of the given class. + * The code source represents the location (URL) from which the class was loaded, + * along with the certificates associated with the code. If the class is {@code null} + * or if the protection domain of the class is {@code null}, this method returns {@code null}. + *

    + * + *

    Example Usage

    + *
    {@code
    +     * CodeSource codeSource = ClassUtils.getCodeSource(String.class);
    +     * // Returns the code source of the String class
    +     *
    +     * CodeSource nullCodeSource = ClassUtils.getCodeSource(null);
    +     * // Returns null since the input class is null
    +     * }
    + * + * @param type the class to get the code source from, may be {@code null} + * @return the code source of the specified class, or {@code null} if the class is {@code null} + * or if the protection domain is {@code null} + * @see ProtectionDomain#getCodeSource() + * @see Class#getProtectionDomain() + */ + @Nullable + public static CodeSource getCodeSource(@Nullable Class type) { + ProtectionDomain protectionDomain = getProtectionDomain(type); + return protectionDomain == null ? null : protectionDomain.getCodeSource(); + } + + /** + * Gets the code source location of the specified class. + *

    + * This method returns the location (URL) from which the given class was loaded. + * The location is obtained from the {@link CodeSource} associated with the class's + * {@link ProtectionDomain}. If the class is {@code null}, or if the protection domain + * or code source is {@code null}, this method returns {@code null}. + *

    + * + *

    Example Usage

    + *
    {@code
    +     * URL location = ClassUtils.getCodeSourceLocation(String.class);
    +     * // Returns the URL location from which the String class was loaded
    +     *
    +     * URL nullLocation = ClassUtils.getCodeSourceLocation(null);
    +     * // Returns null since the input class is null
    +     * }
    + * + * @param type the class to get the code source location from, may be {@code null} + * @return the code source location of the specified class, or {@code null} if the class is {@code null}, + * or if the protection domain or code source is {@code null} + * @see CodeSource#getLocation() + * @see Class#getProtectionDomain() + */ + @Nullable + public static URL getCodeSourceLocation(@Nullable Class type) { + CodeSource codeSource = getCodeSource(type); + return codeSource == null ? null : codeSource.getLocation(); + } + private ClassUtils() { } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java index af755538b..bdfc5c3a1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java @@ -4,13 +4,14 @@ package io.microsphere.util; import io.microsphere.AbstractTestCase; -import io.microsphere.lang.ClassDataRepository; import org.junit.jupiter.api.Test; import java.lang.management.RuntimeMXBean; import java.net.URL; import java.util.Set; +import java.util.function.Predicate; +import static io.microsphere.lang.ClassDataRepository.INSTANCE; import static io.microsphere.util.ClassLoaderUtils.isLoadedClass; import static io.microsphere.util.ClassPathUtils.getBootstrapClassPaths; import static io.microsphere.util.ClassPathUtils.getClassPaths; @@ -57,16 +58,22 @@ void testGetRuntimeClassLocation() { assertNotNull(location); log(location); - //Primitive type + // Primitive type location = getRuntimeClassLocation(int.class); assertNull(location); - //Array type + // Array type location = getRuntimeClassLocation(int[].class); assertNull(location); + // Synthetic type + Predicate predicate = t -> true; + predicate = predicate.negate(); + location = getRuntimeClassLocation(predicate.getClass()); + assertNull(location); + - Set classNames = ClassDataRepository.INSTANCE.getAllClassNamesInClassPaths(); + Set classNames = INSTANCE.getAllClassNamesInClassPaths(); for (String className : classNames) { if (!isLoadedClass(TEST_CLASS_LOADER, className)) { location = getRuntimeClassLocation(className); diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java index d1a9f2441..30addf6f1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java @@ -50,6 +50,9 @@ import static io.microsphere.util.ClassUtils.getAllInterfaces; import static io.microsphere.util.ClassUtils.getAllSuperClasses; import static io.microsphere.util.ClassUtils.getClasses; +import static io.microsphere.util.ClassUtils.getCodeSource; +import static io.microsphere.util.ClassUtils.getCodeSourceLocation; +import static io.microsphere.util.ClassUtils.getProtectionDomain; import static io.microsphere.util.ClassUtils.getSimpleName; import static io.microsphere.util.ClassUtils.getTopComponentType; import static io.microsphere.util.ClassUtils.getType; @@ -83,6 +86,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -776,6 +780,27 @@ void testCast() { assertEquals("test", cast("test", CharSequence.class)); } + @Test + void testGetProtectionDomain() { + assertNull(getProtectionDomain(null)); + assertNotNull(getProtectionDomain(String.class)); + assertNotNull(getProtectionDomain(ClassUtilsTest.class)); + } + + @Test + void testGetCodeSource() { + assertNull(getCodeSource(null)); + assertNull(getCodeSource(String.class)); + assertNotNull(getCodeSource(ClassUtilsTest.class)); + } + + @Test + void testGetCodeSourceLocation() { + assertNull(getCodeSourceLocation(null)); + assertNull(getCodeSourceLocation(String.class)); + assertNotNull(getCodeSourceLocation(ClassUtilsTest.class)); + } + private void assertFindClassNamesMethod (Class targetClassInClassPath, BiFunction> findClassNamesFunction) { // Null From a9c515d742fee025e10eb91f715e9c082f88f762 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 11:39:24 +0800 Subject: [PATCH 093/438] Test PackageNameClassFilter subpackage flag Add unit tests to verify PackageNameClassFilter behavior when includedSubPackages is false: ensure a filter for a parent package (io.microsphere) does not accept classes in subpackages, while a filter for the exact package (io.microsphere.filter) does. Also apply minor whitespace cleanup in PackageNameClassFilter source. --- .../java/io/microsphere/filter/PackageNameClassFilter.java | 2 ++ .../io/microsphere/filter/PackageNameClassFilterTest.java | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/filter/PackageNameClassFilter.java b/microsphere-java-core/src/main/java/io/microsphere/filter/PackageNameClassFilter.java index e77237aa7..c0e313cc9 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/filter/PackageNameClassFilter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/filter/PackageNameClassFilter.java @@ -24,7 +24,9 @@ public class PackageNameClassFilter implements ClassFilter { private final String packageName; + private final boolean includedSubPackages; + private final String subPackageNamePrefix; /** diff --git a/microsphere-java-core/src/test/java/io/microsphere/filter/PackageNameClassFilterTest.java b/microsphere-java-core/src/test/java/io/microsphere/filter/PackageNameClassFilterTest.java index 8b02a5a86..dbd81c0b1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/filter/PackageNameClassFilterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/filter/PackageNameClassFilterTest.java @@ -20,5 +20,11 @@ void testAccept() { assertTrue(filter.accept(PackageNameClassFilterTest.class)); assertFalse(filter.accept(String.class)); assertFalse(filter.accept(null)); + + filter = new PackageNameClassFilter("io.microsphere", false); + assertFalse(filter.accept(PackageNameClassFilterTest.class)); + + filter = new PackageNameClassFilter("io.microsphere.filter", false); + assertTrue(filter.accept(PackageNameClassFilterTest.class)); } } \ No newline at end of file From 0cc7f83e02e60ee7677257c29ef9b64e5cb626db Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 13:08:37 +0800 Subject: [PATCH 094/438] Use instanceof in MultipleType.equals Replace strict getClass check with an instanceof check in MultipleType.equals and add braces for readability. Update tests to reflect behavior: use this.multipleType references, add reflexive and null checks (assertTrue/assertFalse), and adjust assertions accordingly. --- .../java/io/microsphere/reflect/MultipleType.java | 8 ++++++-- .../io/microsphere/reflect/MultipleTypeTest.java | 12 ++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MultipleType.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MultipleType.java index 906a3198c..d4ae35a5a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MultipleType.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MultipleType.java @@ -61,8 +61,12 @@ public int hashCode() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (!(o instanceof MultipleType)) { + return false; + } MultipleType that = (MultipleType) o; return arrayEquals(types, that.types); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MultipleTypeTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MultipleTypeTest.java index 90e054b21..94bab70b8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MultipleTypeTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MultipleTypeTest.java @@ -6,7 +6,9 @@ import static io.microsphere.reflect.MultipleType.of; import static java.util.Objects.hash; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link MultipleType} Test @@ -25,18 +27,20 @@ void setUp() { @Test void testHashCode() { - assertEquals(hash(String.class, Object.class), multipleType.hashCode()); + assertEquals(hash(String.class, Object.class), this.multipleType.hashCode()); } @Test void testEquals() { - assertEquals(of(String.class, Object.class), multipleType); + assertTrue(this.multipleType.equals(this.multipleType)); + assertFalse(this.multipleType.equals(null)); + assertEquals(of(String.class, Object.class), this.multipleType); assertEquals(of(String.class, Object.class, Integer.class), of(String.class, Object.class, Integer.class)); - assertNotEquals(of(String.class, Object.class, Integer.class), multipleType); + assertNotEquals(of(String.class, Object.class, Integer.class), this.multipleType); } @Test void testToString() { - assertEquals("MultipleType : [class java.lang.String, class java.lang.Object]", multipleType.toString()); + assertEquals("MultipleType : [class java.lang.String, class java.lang.Object]", this.multipleType.toString()); } } \ No newline at end of file From 2b4fd3a2050c40232501bdd5f57d45626360aef2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 13:51:50 +0800 Subject: [PATCH 095/438] Make AccessibleObjectUtils methods package-visible Change several helper methods in AccessibleObjectUtils from private to package-private (trySetAccessible, tryCanAccess, handleInaccessibleObjectExceptionIfFound) and add a new tryCanAccess(MethodHandle, ...) overload to allow direct invocation with a MethodHandle. Update unit tests to exercise these paths: import and call the newly-visible helpers, add null/failure cases for trySetAccessible and tryCanAccess, assert canAccess behavior on non-public members, and ensure handleInaccessibleObjectExceptionIfFound handles a non-related exception. These changes improve testability and allow invoking internal logic from tests. --- .../reflect/AccessibleObjectUtils.java | 14 +++++++---- .../reflect/AccessibleObjectUtilsTest.java | 25 ++++++++++++++++++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/AccessibleObjectUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/AccessibleObjectUtils.java index 4bb741d3b..d6ea3d485 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/AccessibleObjectUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/AccessibleObjectUtils.java @@ -167,7 +167,7 @@ static boolean setAccessible(AccessibleObject accessibleObject) { return accessible; } - private static boolean trySetAccessible(MethodHandle methodHandle, AccessibleObject accessibleObject) { + static boolean trySetAccessible(MethodHandle methodHandle, AccessibleObject accessibleObject) { boolean accessible = false; try { accessible = (boolean) methodHandle.invokeExact(accessibleObject); @@ -177,11 +177,15 @@ private static boolean trySetAccessible(MethodHandle methodHandle, AccessibleObj return accessible; } - private static Boolean tryCanAccess(Object object, AccessibleObject accessibleObject) { + static Boolean tryCanAccess(Object object, AccessibleObject accessibleObject) { + return tryCanAccess(canAccessMethodHandle, object, accessibleObject); + } + + static Boolean tryCanAccess(MethodHandle methodHandle, Object object, AccessibleObject accessibleObject) { Boolean access = null; - if (canAccessMethodHandle != null) { // JDK 9+ + if (methodHandle != null) { // JDK 9+ try { - access = (boolean) canAccessMethodHandle.invokeExact(accessibleObject, object); + access = (boolean) methodHandle.invokeExact(accessibleObject, object); } catch (Throwable e) { logger.error("It's failed to invokeExact on {} with object : {} , accessible object : {}", canAccessMethodHandle, object, accessibleObject, e); } @@ -189,7 +193,7 @@ private static Boolean tryCanAccess(Object object, AccessibleObject accessibleOb return access; } - private static void handleInaccessibleObjectExceptionIfFound(Throwable e) { + static void handleInaccessibleObjectExceptionIfFound(Throwable e) { if (isInaccessibleObjectException(e)) { String rawErrorMessage = e.getMessage(); String moduleName = substringBetween(rawErrorMessage, "module ", SPACE); diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/AccessibleObjectUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/AccessibleObjectUtilsTest.java index 57a4e007e..18e4bc7d2 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/AccessibleObjectUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/AccessibleObjectUtilsTest.java @@ -24,13 +24,16 @@ import java.lang.reflect.Method; import static io.microsphere.reflect.AccessibleObjectUtils.canAccess; +import static io.microsphere.reflect.AccessibleObjectUtils.handleInaccessibleObjectExceptionIfFound; import static io.microsphere.reflect.AccessibleObjectUtils.setAccessible; +import static io.microsphere.reflect.AccessibleObjectUtils.tryCanAccess; import static io.microsphere.reflect.AccessibleObjectUtils.trySetAccessible; import static io.microsphere.reflect.ConstructorUtils.findConstructor; import static io.microsphere.reflect.MemberUtils.isStatic; import static io.microsphere.reflect.MethodUtils.findMethod; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -84,6 +87,10 @@ void testCanAccess() { @Test void testCanAccessOnNonPublicMembers() { assertFalse(canAccess(null, tryCanAccessMethod)); + + Method cloneMethod = findMethod(Object.class, "clone"); + assertFalse(canAccess(null, cloneMethod)); + assertFalse(canAccess(test, cloneMethod)); } @Test @@ -98,4 +105,20 @@ void testTrySetAccessibleOnNonPublicMembers() { assertTrue(trySetAccessible(tryCanAccessMethod)); assertTrue(trySetAccessible(abstractProcessorConstructor)); } -} + + @Test + void testTrySetAccessibleOnFailed() { + assertFalse(trySetAccessible(null, null)); + } + + @Test + void testTryCanAccessOnFailed() { + assertNull(tryCanAccess(null, null)); + assertNull(tryCanAccess(null, null, null)); + } + + @Test + void testHandleInaccessibleObjectExceptionIfFound() { + handleInaccessibleObjectExceptionIfFound(new RuntimeException("For testing")); + } +} \ No newline at end of file From 16c83da42999d8cd976f83a5ea460843bda5c3db Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 14:13:35 +0800 Subject: [PATCH 096/438] Use Predicate.negate() and Objects.nonNull Refactor LoggerFactory to use a Predicate for availability filtering and Predicate.negate() in loadAvailableFactories(), and replace explicit null check with Objects.nonNull() in isAvailable(). Added imports for java.util.function.Predicate and java.util.Objects.nonNull for readability. --- .../main/java/io/microsphere/logging/LoggerFactory.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/logging/LoggerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/logging/LoggerFactory.java index 1ffe2c503..2adadb1ed 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/logging/LoggerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/logging/LoggerFactory.java @@ -21,9 +21,11 @@ import io.microsphere.lang.Prioritized; import java.util.List; +import java.util.function.Predicate; import static io.microsphere.collection.ListUtils.newLinkedList; import static java.util.Collections.sort; +import static java.util.Objects.nonNull; import static java.util.ServiceLoader.load; /** @@ -74,7 +76,8 @@ private static LoggerFactory loadFactory() { static List loadAvailableFactories() { List factories = loadFactories(); - factories.removeIf(factory -> !factory.isAvailable()); + Predicate predicate = LoggerFactory::isAvailable; + factories.removeIf(predicate.negate()); return factories; } @@ -112,7 +115,7 @@ public static Logger getLogger(String name) { * @return true if available */ protected boolean isAvailable() { - return getDelegateLoggerClass() != null; + return nonNull(getDelegateLoggerClass()); } /** From 5f95752a04f15d5e749bdf186d58f5253791571e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 14:23:13 +0800 Subject: [PATCH 097/438] Add NoDelegateLoggerFactory and service entry Add a test LoggerFactory implementation (NoDelegateLoggerFactory) that reports a non-existent delegate logger class and returns null loggers. Register the implementation in test resources via META-INF/services/io.microsphere.logging.LoggerFactory to exercise service-loading and fallback behavior when the delegate logger class cannot be found. --- .../logging/NoDelegateLoggerFactory.java | 38 +++++++++++++++++++ .../io.microsphere.logging.LoggerFactory | 1 + 2 files changed, 39 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/logging/NoDelegateLoggerFactory.java create mode 100644 microsphere-java-core/src/test/resources/META-INF/services/io.microsphere.logging.LoggerFactory diff --git a/microsphere-java-core/src/test/java/io/microsphere/logging/NoDelegateLoggerFactory.java b/microsphere-java-core/src/test/java/io/microsphere/logging/NoDelegateLoggerFactory.java new file mode 100644 index 000000000..68a81ff43 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/logging/NoDelegateLoggerFactory.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.microsphere.logging; + +/** + * No Delegate {@link LoggerFactory} + * + * @author Mercy + * @see LoggerFactory + * @since 1.0.0 + */ +public class NoDelegateLoggerFactory extends LoggerFactory { + + @Override + protected String getDelegateLoggerClassName() { + return "class-not-found"; + } + + @Override + public Logger createLogger(String name) { + return null; + } +} diff --git a/microsphere-java-core/src/test/resources/META-INF/services/io.microsphere.logging.LoggerFactory b/microsphere-java-core/src/test/resources/META-INF/services/io.microsphere.logging.LoggerFactory new file mode 100644 index 000000000..3c3e4d472 --- /dev/null +++ b/microsphere-java-core/src/test/resources/META-INF/services/io.microsphere.logging.LoggerFactory @@ -0,0 +1 @@ +io.microsphere.logging.NoDelegateLoggerFactory \ No newline at end of file From 6c3dd269aa735706a587e27edf92c8e8155fc854 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 7 Feb 2026 18:56:20 +0800 Subject: [PATCH 098/438] Simplify getFileExtension; refactor FileUtils tests Replace manual extension parsing with substringAfterLast(FILE_EXTENSION) in FileUtils.getFileExtension and update imports accordingly. Refactor FileUtilsTest to consolidate flaky concurrent directory-deletion tests into reusable helpers (handleDirectoryOnIOException and handleDirectoryOnIOException0), add makeLinkFile to create symlinks cross-platform, use ThrowableConsumer for injected handlers, improve status readability with named constants, simplify isSymlink test, and clean up threading/exception handling and logging. These changes make the extension logic simpler and the tests more robust and reusable. --- .../java/io/microsphere/io/FileUtils.java | 10 +- .../java/io/microsphere/io/FileUtilsTest.java | 174 +++++++++++------- 2 files changed, 108 insertions(+), 76 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/FileUtils.java b/microsphere-java-core/src/main/java/io/microsphere/io/FileUtils.java index b8ca0539e..8c967578a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/FileUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/FileUtils.java @@ -13,13 +13,13 @@ import java.io.FileNotFoundException; import java.io.IOException; -import static io.microsphere.constants.FileConstants.FILE_EXTENSION_CHAR; +import static io.microsphere.constants.FileConstants.FILE_EXTENSION; import static io.microsphere.constants.PathConstants.SLASH_CHAR; import static io.microsphere.constants.SymbolConstants.DOT_CHAR; import static io.microsphere.lang.function.ThrowableSupplier.execute; import static io.microsphere.util.ArrayUtils.isEmpty; import static io.microsphere.util.CharSequenceUtils.isEmpty; -import static io.microsphere.util.StringUtils.isBlank; +import static io.microsphere.util.StringUtils.substringAfterLast; import static java.io.File.separatorChar; import static java.nio.file.Files.isSymbolicLink; @@ -97,11 +97,7 @@ public static String resolveRelativePath(File parentDirectory, File targetFile) */ @Nullable public static String getFileExtension(String fileName) { - if (isBlank(fileName)) { - return null; - } - int index = fileName.lastIndexOf(FILE_EXTENSION_CHAR); - return index > -1 ? fileName.substring(index + 1) : null; + return substringAfterLast(fileName, FILE_EXTENSION); } /** diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index 80207a407..8faa7905a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -1,6 +1,7 @@ package io.microsphere.io; import io.microsphere.AbstractTestCase; +import io.microsphere.lang.function.ThrowableConsumer; import io.microsphere.process.ProcessExecutor; import org.junit.jupiter.api.Test; @@ -27,6 +28,7 @@ import static io.microsphere.io.FileUtils.resolveRelativePath; import static io.microsphere.util.ClassLoaderUtils.getClassResource; import static io.microsphere.util.ClassLoaderUtils.getResource; +import static io.microsphere.util.ExceptionUtils.wrap; import static io.microsphere.util.StringUtils.EMPTY_STRING; import static io.microsphere.util.SystemUtils.IS_OS_WINDOWS; import static io.microsphere.util.SystemUtils.JAVA_IO_TMPDIR; @@ -114,63 +116,14 @@ void testDeleteDirectoryOnNotExists() throws IOException { @Test void testDeleteDirectoryOnIOException() throws Exception { - ExecutorService executor = newSingleThreadExecutor(); - executor.submit(this::testDeleteDirectoryOnIOException0); - shutdown(executor); - executor.awaitTermination(5, SECONDS); + handleDirectoryOnIOException(FileUtils::deleteDirectory); } - File testDeleteDirectoryOnIOException0() throws Exception { + @Test + void testDeleteDirectoryOnLink() throws IOException { File testDir = createRandomTempDirectory(); - - ExecutorService fileCreationExecutor = newSingleThreadExecutor(); - - AtomicBoolean creatingFile = new AtomicBoolean(true); - - long waitTime = 50L; - - fileCreationExecutor.submit(() -> { - while (creatingFile.get()) { - createRandomFile(testDir); - } - return null; - }); - - AtomicReference ioExceptionReference = new AtomicReference<>(); - - AtomicBoolean deletingDirectory = new AtomicBoolean(true); - - ExecutorService directoryDeletionExecutor = newSingleThreadExecutor(); - - directoryDeletionExecutor.submit(() -> { - while (deletingDirectory.get()) { - try { - deleteDirectory(testDir); - sleep(waitTime / 10); - } catch (IOException e) { - ioExceptionReference.set(e); - creatingFile.set(false); - deletingDirectory.set(false); - break; - } - } - return null; - }); - - for (int i = 0; i < 100; i++) { - if (creatingFile.get()) { - sleep(waitTime); - } - } - - shutdown(fileCreationExecutor); - shutdown(directoryDeletionExecutor); - - assertNotNull(ioExceptionReference.get()); - assertFalse(creatingFile.get()); - assertFalse(deletingDirectory.get()); - - return testDir; + File linkDir = makeLinkFile(testDir); + assertEquals(1, deleteDirectory(linkDir)); } @Test @@ -197,6 +150,11 @@ void testCleanDirectoryOnFile() throws IOException { assertEquals(0, cleanDirectory(tempFile)); } + @Test + void testCleanDirectoryOnIOException() throws Exception { + handleDirectoryOnIOException(FileUtils::cleanDirectory); + } + @Test void testForceDeleteOnEmptyDirectory() throws IOException { File tempDir = createRandomTempDirectory(); @@ -237,13 +195,16 @@ void testForceDeleteOnIOException() throws Exception { // status : 0 -> init // status : 1 -> writing // status : 2 -> deleting - AtomicInteger status = new AtomicInteger(0); + int init = 0; + int writing = 1; + int deleting = 2; + AtomicInteger status = new AtomicInteger(init); executor.submit(() -> { synchronized (testFile) { try (FileOutputStream outputStream = new FileOutputStream(testFile, true)) { outputStream.write('a'); - status.set(1); + status.set(writing); // wait for notification testFile.wait(); } @@ -252,15 +213,15 @@ void testForceDeleteOnIOException() throws Exception { }); executor.submit(() -> { - while (status.get() != 1) { + while (status.get() != writing) { } assertThrows(IOException.class, () -> forceDelete(testFile)); - status.set(2); + status.set(deleting); return null; }); executor.submit(() -> { - while (status.get() != 2) { + while (status.get() != deleting) { } synchronized (testFile) { testFile.notify(); @@ -299,16 +260,10 @@ void testDeleteDirectoryOnExitOnNotExists() throws IOException { @Test void testIsSymlink() throws IOException { - if (IS_OS_WINDOWS) { - assertFalse(isSymlink(new File(""))); - } else { - File tempDir = createRandomTempDirectory(); - File targetFile = createRandomFile(tempDir); - File linkFile = new File(tempDir, "link"); - ProcessExecutor processExecutor = new ProcessExecutor("ln", "-s", targetFile.getAbsolutePath(), linkFile.getAbsolutePath()); - processExecutor.execute(System.out); - assertTrue(isSymlink(linkFile)); - } + File tempDir = createRandomTempDirectory(); + File targetFile = createRandomFile(tempDir); + File linkFile = makeLinkFile(targetFile); + assertTrue(isSymlink(linkFile)); } @Test @@ -322,4 +277,85 @@ void testGetCanonicalFile() { assertEquals(getCanonicalFile(tempFile), getCanonicalFile(getCanonicalFile(tempFile))); } + File makeLinkFile(File targetFile) throws IOException { + File tempDir = createRandomTempDirectory(); + File linkFile = new File(tempDir, "link"); + boolean directory = targetFile.isDirectory(); + String targetPatth = targetFile.getAbsolutePath(); + String linkPath = linkFile.getAbsolutePath(); + final ProcessExecutor processExecutor; + if (IS_OS_WINDOWS) { + processExecutor = directory ? + new ProcessExecutor("mklink", "/D", targetPatth, linkPath) : + new ProcessExecutor("mklink", targetPatth, linkPath); + } else { + processExecutor = new ProcessExecutor("ln", "-s", targetPatth, linkPath); + } + processExecutor.execute(System.out); + return linkFile; + } + + void handleDirectoryOnIOException(ThrowableConsumer directoryHandler) throws Exception { + ExecutorService executor = newSingleThreadExecutor(); + executor.submit(() -> handleDirectoryOnIOException0(directoryHandler)); + shutdown(executor); + executor.awaitTermination(3, SECONDS); + } + + File handleDirectoryOnIOException0(ThrowableConsumer directoryHandler) throws Exception { + File testDir = createRandomTempDirectory(); + + ExecutorService fileCreationExecutor = newSingleThreadExecutor(); + + AtomicBoolean creatingFile = new AtomicBoolean(true); + + long waitTime = 50L; + + fileCreationExecutor.submit(() -> { + while (creatingFile.get()) { + createRandomFile(testDir); + } + return null; + }); + + AtomicReference ioExceptionReference = new AtomicReference<>(); + + AtomicBoolean deletingDirectory = new AtomicBoolean(true); + + ExecutorService directoryDeletionExecutor = newSingleThreadExecutor(); + + directoryDeletionExecutor.submit(() -> { + while (deletingDirectory.get()) { + try { + directoryHandler.accept(testDir); + sleep(waitTime / 10); + } catch (IOException e) { + ioExceptionReference.set(e); + creatingFile.set(false); + deletingDirectory.set(false); + logger.trace(e.getMessage(), e); + break; + } catch (Throwable e) { + throw wrap(e, Exception.class); + } + } + return null; + }); + + for (int i = 0; i < 100; i++) { + if (creatingFile.get()) { + sleep(waitTime); + } + } + + shutdown(fileCreationExecutor); + shutdown(directoryDeletionExecutor); + + assertNotNull(ioExceptionReference.get()); + assertFalse(creatingFile.get()); + assertFalse(deletingDirectory.get()); + + return testDir; + } + } \ No newline at end of file From 3c52a82ceb6eac832ae085902a201ad6df5ac935 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 8 Feb 2026 14:44:40 +0800 Subject: [PATCH 099/438] Use NIO Files.delete and update FileUtils tests Replace legacy File.delete logic with java.nio Files.delete in FileUtils.forceDelete and surface NoSuchFileException instead of FileNotFoundException. Simplify cleanDirectory to let deletion exceptions propagate immediately (removed exception aggregation). Add necessary imports for NIO deletion. Update tests: enable parallel execution for FileUtilsTest, add listFiles tests (null, not-found, directory, file), import EMPTY_FILE_ARRAY and listFiles utilities, add a symlink case to deleteDirectoryOnExit test, and shorten executor.awaitTermination from 3s to 1s. Remove the old test that handled cleanDirectory IOException. --- .../java/io/microsphere/io/FileUtils.java | 36 +++++----------- .../java/io/microsphere/io/FileUtilsTest.java | 43 ++++++++++++++++--- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/FileUtils.java b/microsphere-java-core/src/main/java/io/microsphere/io/FileUtils.java index 8c967578a..b11cf416a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/FileUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/FileUtils.java @@ -10,8 +10,8 @@ import io.microsphere.util.Utils; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.NoSuchFileException; import static io.microsphere.constants.FileConstants.FILE_EXTENSION; import static io.microsphere.constants.PathConstants.SLASH_CHAR; @@ -21,6 +21,7 @@ import static io.microsphere.util.CharSequenceUtils.isEmpty; import static io.microsphere.util.StringUtils.substringAfterLast; import static java.io.File.separatorChar; +import static java.nio.file.Files.delete; import static java.nio.file.Files.isSymbolicLink; /** @@ -160,17 +161,8 @@ public static int deleteDirectory(File directory) throws IOException { */ public static int cleanDirectory(File directory) throws IOException { int deletedFilesCount = 0; - IOException exception = null; for (File file : listFiles(directory)) { - try { - deletedFilesCount += forceDelete(file); - } catch (IOException ioe) { - exception = ioe; - } - } - - if (null != exception) { - throw exception; + deletedFilesCount += forceDelete(file); } return deletedFilesCount; } @@ -186,30 +178,22 @@ public static int cleanDirectory(File directory) throws IOException { *
  • {@code forceDelete(new File("/tmp/file.txt"))} deletes the file and returns {@code 1}
  • *
  • {@code forceDelete(new File("/tmp/testDir"))} deletes the directory and all its contents, * returning the total count of deleted files and directories.
  • - *
  • {@code forceDelete(new File("/nonexistent/file"))} throws a {@link FileNotFoundException}
  • + *
  • {@code forceDelete(new File("/nonexistent/file"))} throws a {@link NoSuchFileException}
  • * * * @param file the file or directory to delete, must not be {@code null} * @return the number of deleted files and directories - * @throws NullPointerException if the file is {@code null} - * @throws FileNotFoundException if the file does not exist - * @throws IOException if deletion fails for any reason + * @throws NullPointerException if the file is {@code null} + * @throws NoSuchFileException if the file does not exist + * @throws IOException if deletion fails for any reason */ - public static int forceDelete(File file) throws IOException { + public static int forceDelete(File file) throws NoSuchFileException, IOException { final int deletedFilesCount; if (file.isDirectory()) { deletedFilesCount = deleteDirectory(file); } else { - boolean filePresent = file.exists(); - if (file.delete()) { - deletedFilesCount = 1; - } else { - if (!filePresent) { - throw new FileNotFoundException("File does not exist: " + file); - } - String message = "Unable to delete file: " + file; - throw new IOException(message); - } + delete(file.toPath()); + deletedFilesCount = 1; } return deletedFilesCount; } diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index 8faa7905a..d5d855e28 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -4,6 +4,7 @@ import io.microsphere.lang.function.ThrowableConsumer; import io.microsphere.process.ProcessExecutor; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import java.io.File; import java.io.FileOutputStream; @@ -17,6 +18,7 @@ import static io.microsphere.concurrent.CustomizedThreadFactory.newThreadFactory; import static io.microsphere.concurrent.ExecutorUtils.shutdown; import static io.microsphere.constants.FileConstants.CLASS; +import static io.microsphere.io.FileUtils.EMPTY_FILE_ARRAY; import static io.microsphere.io.FileUtils.cleanDirectory; import static io.microsphere.io.FileUtils.deleteDirectory; import static io.microsphere.io.FileUtils.deleteDirectoryOnExit; @@ -25,7 +27,9 @@ import static io.microsphere.io.FileUtils.getCanonicalFile; import static io.microsphere.io.FileUtils.getFileExtension; import static io.microsphere.io.FileUtils.isSymlink; +import static io.microsphere.io.FileUtils.listFiles; import static io.microsphere.io.FileUtils.resolveRelativePath; +import static io.microsphere.util.ArrayUtils.isNotEmpty; import static io.microsphere.util.ClassLoaderUtils.getClassResource; import static io.microsphere.util.ClassLoaderUtils.getResource; import static io.microsphere.util.ExceptionUtils.wrap; @@ -43,6 +47,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; /** * {@link FileUtils} Test @@ -51,6 +56,7 @@ * @see FileUtils * @since 1.0.0 */ +@Execution(CONCURRENT) class FileUtilsTest extends AbstractTestCase { private final URL classFileResource = getClassResource(TEST_CLASS_LOADER, FileUtilsTest.class); @@ -150,11 +156,6 @@ void testCleanDirectoryOnFile() throws IOException { assertEquals(0, cleanDirectory(tempFile)); } - @Test - void testCleanDirectoryOnIOException() throws Exception { - handleDirectoryOnIOException(FileUtils::cleanDirectory); - } - @Test void testForceDeleteOnEmptyDirectory() throws IOException { File tempDir = createRandomTempDirectory(); @@ -169,6 +170,32 @@ void testForceDeleteOnSingleFile() throws IOException { assertFalse(tempFile.exists()); } + @Test + void testListFiles() throws IOException { + File testDir = createRandomTempDirectory(); + assertEquals(EMPTY_FILE_ARRAY, listFiles(testDir)); + + createRandomFile(testDir); + assertTrue(isNotEmpty(listFiles(testDir))); + + deleteDirectoryOnExit(testDir); + } + + @Test + void testListFilesOnNull() { + assertEquals(EMPTY_FILE_ARRAY, listFiles(null)); + } + + @Test + void testListFilesOnNotFound() { + assertEquals(EMPTY_FILE_ARRAY, listFiles(new File("not-found"))); + } + + @Test + void testListFilesOnFile() throws IOException { + assertEquals(EMPTY_FILE_ARRAY, listFiles(createRandomTempFile())); + } + @Test void testForceDelete() throws IOException { File testDir = createRandomTempDirectory(); @@ -243,6 +270,10 @@ void testForceDeleteOnExit() throws IOException { @Test void testDeleteDirectoryOnExit() throws IOException { File tempDir = createRandomTempDirectory(); + + File link = makeLinkFile(tempDir); + deleteDirectoryOnExit(link); + for (int i = 0; i < 10; i++) { if (i % 2 == 0) { createRandomDirectory(tempDir); @@ -299,7 +330,7 @@ void handleDirectoryOnIOException(ThrowableConsumer directoryHandler) thro ExecutorService executor = newSingleThreadExecutor(); executor.submit(() -> handleDirectoryOnIOException0(directoryHandler)); shutdown(executor); - executor.awaitTermination(3, SECONDS); + executor.awaitTermination(1, SECONDS); } File handleDirectoryOnIOException0(ThrowableConsumer directoryHandler) throws Exception { From 9806353fdeeee8d1e472fc84ff189765d39bdae2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 8 Feb 2026 17:34:31 +0800 Subject: [PATCH 100/438] Remove @Execution(CONCURRENT) from FileUtilsTest Remove the JUnit Jupiter @Execution(CONCURRENT) annotation from FileUtilsTest to disable class-level concurrent execution. This prevents potential flaky tests or resource contention when running this test class in parallel. --- .../src/test/java/io/microsphere/io/FileUtilsTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index d5d855e28..b4832cd1e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -56,7 +56,6 @@ * @see FileUtils * @since 1.0.0 */ -@Execution(CONCURRENT) class FileUtilsTest extends AbstractTestCase { private final URL classFileResource = getClassResource(TEST_CLASS_LOADER, FileUtilsTest.class); From c4fe79caaa31ce0d5ebf1e69129e6bb741281b45 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 8 Feb 2026 17:34:45 +0800 Subject: [PATCH 101/438] Remove JUnit parallel execution imports in test Delete unused org.junit.jupiter.api.parallel.Execution and ExecutionMode.CONCURRENT imports from FileUtilsTest to clean up the test file and avoid accidental parallel execution annotations. No functional changes. --- .../src/test/java/io/microsphere/io/FileUtilsTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index b4832cd1e..83e81f5b5 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -4,7 +4,6 @@ import io.microsphere.lang.function.ThrowableConsumer; import io.microsphere.process.ProcessExecutor; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.Execution; import java.io.File; import java.io.FileOutputStream; @@ -47,7 +46,6 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; /** * {@link FileUtils} Test From 84effee2a3cbc94991ae16a0d235b95ff0250e00 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 01:26:50 +0800 Subject: [PATCH 102/438] Refactor EventListener type resolution and tests Use TypeUtils helpers to simplify and null-safe type resolution in EventListener: replace manual instanceof/Class checks with isAssignableFrom and asClass, and add static imports for clarity. Adjust logic to handle null listener classes and non-Class type arguments more robustly. Add tests: verify findEventType returns null for null input and assert default listener priority equals NORMAL_PRIORITY. --- .../io/microsphere/event/EventListener.java | 17 +++++++-------- .../microsphere/event/EventListenerTest.java | 21 ++++++++++++++++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java b/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java index 53bf32623..c85f1a5f2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java +++ b/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java @@ -22,8 +22,9 @@ import java.lang.reflect.Type; import java.util.Objects; +import static io.microsphere.reflect.TypeUtils.asClass; import static io.microsphere.reflect.TypeUtils.getAllParameterizedTypes; - +import static io.microsphere.reflect.TypeUtils.isAssignableFrom; /** * The {@link Event Event} Listener that is based on Java standard {@link java.util.EventListener} interface supports @@ -128,7 +129,7 @@ static Class findEventType(EventListener listener) { static Class findEventType(Class listenerClass) { Class eventType = null; - if (listenerClass != null && EventListener.class.isAssignableFrom(listenerClass)) { + if (isAssignableFrom(EventListener.class, listenerClass)) { eventType = getAllParameterizedTypes(listenerClass) .stream() .map(EventListener::findEventType) @@ -174,15 +175,13 @@ static Class findEventType(ParameterizedType parameterizedType) Class eventType = null; Type rawType = parameterizedType.getRawType(); - if ((rawType instanceof Class) && EventListener.class.isAssignableFrom((Class) rawType)) { + if (isAssignableFrom(EventListener.class, rawType)) { Type[] typeArguments = parameterizedType.getActualTypeArguments(); for (Type typeArgument : typeArguments) { - if (typeArgument instanceof Class) { - Class argumentClass = (Class) typeArgument; - if (Event.class.isAssignableFrom(argumentClass)) { - eventType = argumentClass; - break; - } + Class argumentClass = asClass(typeArgument); + if (isAssignableFrom(Event.class, argumentClass)) { + eventType = argumentClass; + break; } } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/EventListenerTest.java b/microsphere-java-core/src/test/java/io/microsphere/event/EventListenerTest.java index 77ed943f7..a916c5528 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/EventListenerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/EventListenerTest.java @@ -19,7 +19,9 @@ import org.junit.jupiter.api.Test; import static io.microsphere.event.EventListener.findEventType; +import static io.microsphere.lang.Prioritized.NORMAL_PRIORITY; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; /** @@ -39,7 +41,24 @@ void testFindEventHierarchicalTypes() { } @Test - void testOnEvent() { + void testFindEventTypeOnNull() { + assertNull(findEventType((Class) null)); + } + + @Test + void testGetPriority() { + EventListener listener = new EventListener() { + @Override + public void onEvent(Event event) { + } + + @Override + public int getPriority() { + return EventListener.super.getPriority(); + } + }; + + assertEquals(NORMAL_PRIORITY, listener.getPriority()); } } \ No newline at end of file From fe98ceb57a795b5d7ba0be4cf977e19fc2a9c5af Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 02:17:24 +0800 Subject: [PATCH 103/438] Refactor manifest artifact resolution and add test Replace manual loops with Stream-based lookups for artifactId and version, simplify resolve methods, and move trace logging to the caller. Change resolveArtifactMetaInfoInManifest visibility to package-private to allow testing and add a unit test covering the not-found (empty Manifest) case. --- .../ManifestArtifactResourceResolver.java | 49 +++++++------------ .../ManifestArtifactResourceResolverTest.java | 13 +++++ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/ManifestArtifactResourceResolver.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/ManifestArtifactResourceResolver.java index b0c616a2e..97f79ff08 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/ManifestArtifactResourceResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/ManifestArtifactResourceResolver.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.Objects; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -31,6 +32,7 @@ import static io.microsphere.util.StringUtils.split; import static io.microsphere.util.jar.JarUtils.MANIFEST_RESOURCE_PATH; import static java.lang.System.getProperty; +import static java.util.stream.Stream.of; /** * {@link ArtifactResourceResolver} implementation that reads artifact metadata from JAR manifest files. @@ -197,48 +199,33 @@ protected Artifact resolve(URL resourceURL, InputStream artifactMetadataData, Cl return resolveArtifactMetaInfoInManifest(manifest, resourceURL); } - private Artifact resolveArtifactMetaInfoInManifest(Manifest manifest, URL resourceURL) throws IOException { + Artifact resolveArtifactMetaInfoInManifest(Manifest manifest, URL resourceURL) { Attributes mainAttributes = manifest.getMainAttributes(); - String artifactId = resolveArtifactId(mainAttributes, resourceURL); + String artifactId = resolveArtifactId(mainAttributes); if (artifactId == null) { return null; } String version = resolveVersion(mainAttributes); - return create(artifactId, version, resourceURL); + Artifact artifact = create(artifactId, version, resourceURL); + logger.trace("The artifactId was resolved from the resource URL['{}']: {}", artifactId, artifact); + return artifact; } - private String resolveArtifactId(Attributes attributes, URL artifactResourceURL) { - String artifactId = null; - - for (String artifactIdAttributeName : ARTIFACT_ID_ATTRIBUTE_NAMES) { - artifactId = attributes.getValue(artifactIdAttributeName); - if (artifactId != null) { - break; - } - } - - if (logger.isTraceEnabled()) { - logger.trace("The artifactId was resolved from the '{}' of resource URL['{}'] of : {} , attributes : {}", - MANIFEST_RESOURCE_PATH, - artifactResourceURL.getPath(), - artifactId, - attributes.entrySet() - ); - } - + private String resolveArtifactId(Attributes attributes) { + String artifactId = of(ARTIFACT_ID_ATTRIBUTE_NAMES) + .map(attributes::getValue) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); return artifactId; } private String resolveVersion(Attributes attributes) { - String version = null; - - for (String versionAttributeName : VERSION_ATTRIBUTE_NAMES) { - version = attributes.getValue(versionAttributeName); - if (version != null) { - break; - } - } - + String version = of(VERSION_ATTRIBUTE_NAMES) + .map(attributes::getValue) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); return version; } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/ManifestArtifactResourceResolverTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/ManifestArtifactResourceResolverTest.java index 882363396..91edaf153 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/ManifestArtifactResourceResolverTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/ManifestArtifactResourceResolverTest.java @@ -1,6 +1,11 @@ package io.microsphere.classloading; +import org.junit.jupiter.api.Test; + +import java.util.jar.Manifest; + import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; /** * {@link ManifestArtifactResourceResolver} Test @@ -16,4 +21,12 @@ void assertArtifact(Artifact artifact) { assertEquals("FindBugs-jsr305", artifact.getArtifactId()); assertEquals(TEST_VERSION, artifact.getVersion()); } + + @Test + void testresolveArtifactMetaInfoInManifestOnNotFound() { + ManifestArtifactResourceResolver resolver = this.resolver; + Manifest manifest = new Manifest(); + Artifact artifact = resolver.resolveArtifactMetaInfoInManifest(manifest, null); + assertNull(artifact); + } } \ No newline at end of file From 89e3687b3f118b037450f948ff673490af4582d1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 03:17:32 +0800 Subject: [PATCH 104/438] Simplify logging and add null-handling tests Remove redundant logger.isTraceEnabled() guards and always emit trace messages in FieldUtils for field lookup and error handling. Add unit tests to cover null and Object.class cases (testFindFieldsOnNull, testFindFieldsOnObjectClass) and null field retrieval (testGetFieldValueOnNull), and strengthen assertions around setFieldValue to verify its return value. These changes improve traceability and increase test coverage for null-safety behavior. --- .../io/microsphere/reflect/FieldUtils.java | 16 ++++------------ .../io/microsphere/reflect/FieldUtilsTest.java | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java index ec92e0425..fe139f1d3 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java @@ -115,11 +115,7 @@ public static Field findField(Class klass, String fieldName) { // ignore, try the super class field = findField(klass.getSuperclass(), fieldName); } - if (field == null) { - if (logger.isTraceEnabled()) { - logger.trace("The field[name :'{}'] not found in class : '{}'", fieldName, klass); - } - } + logger.trace("To find the field[name :'{}'] from the class : '{}'", fieldName, klass); return field; } @@ -686,21 +682,17 @@ public static void assertFieldMatchType(Object instance, String fieldName, Class static void handleIllegalAccessException(IllegalAccessException e, Object instance, Field field, boolean accessible) { String errorMessage = format("The instance [object : {} , class : {} ] can't access the field[name : '{}' , type : {} , accessible : {}]", instance, getTypeName(instance.getClass()), field.getName(), getTypeName(field.getType()), accessible); - if (logger.isTraceEnabled()) { - logger.trace(errorMessage); - } + logger.trace(errorMessage); throw new IllegalStateException(errorMessage, e); } static void handleIllegalArgumentException(IllegalArgumentException e, Object instance, Field field) { String errorMessage = format("The instance[object : {} , class : {}] can't match the field[name : '{}' , type : {}]", instance, getTypeName(instance.getClass()), field.getName(), getTypeName(field.getType())); - if (logger.isTraceEnabled()) { - logger.trace(errorMessage); - } + logger.trace(errorMessage); throw new IllegalArgumentException(errorMessage, e); } private FieldUtils() { } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java index cb2e503fd..27be787b9 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java @@ -107,6 +107,15 @@ void testFindFieldOnPredicate() { assertFindField(ReflectionTest.class, "staticField", false); } + @Test + void testFindFieldsOnNull() { + assertNull(findField(null, null)); + } + + @Test + void testFindFieldsOnObjectClass() { + assertNull(findField(Object.class, null)); + } @Test void testFindAllFields() { @@ -200,11 +209,17 @@ void testGetFieldValueOnIllegalArgumentException() { assertThrows(IllegalArgumentException.class, () -> getFieldValue("test", field)); } + @Test + void testGetFieldValueOnNull() { + Object fieldValue = getFieldValue(null, (Field) null); + assertNull(fieldValue); + } + @Test void testSetFieldValue() { Integer value = 999; setFieldValue(value, "value", 2); - assertEquals(value.intValue(), 2); + assertEquals(value.intValue(), setFieldValue(value, "value", 2)); assertSetFieldValue(test, "privateField", "test"); assertSetFieldValue(test, "packagePrivateField", "test"); @@ -274,6 +289,7 @@ private void assertGetStaticFieldValue(Class klass, String fieldName) { private void assertGetFieldValue(ReflectionTest test, String fieldName) { assertEquals(fieldName, getFieldValue(test, fieldName)); + assertEquals(fieldName, getFieldValue(test, fieldName, (Object) null)); } private void assertGetFieldValue(ReflectionTest test, String fieldName, Object defaultValue) { From 45a7b348aae678db4698c342da2b2d3590c6d3f9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 15:06:48 +0800 Subject: [PATCH 105/438] Extract makeLinkFile into AbstractTestCase Move the makeLinkFile helper from FileUtilsTest into AbstractTestCase as a protected method so tests can reuse it. Add ProcessExecutor and IS_OS_WINDOWS imports and implementation to create symlinks (ln -s) or Windows mklink accordingly. Remove the duplicate helper and unused imports from FileUtilsTest. --- .../java/io/microsphere/AbstractTestCase.java | 20 +++++++++++++++++++ .../java/io/microsphere/io/FileUtilsTest.java | 20 ------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java index 154b18633..048288427 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java +++ b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java @@ -5,6 +5,7 @@ import io.microsphere.lang.function.ThrowableAction; import io.microsphere.logging.Logger; +import io.microsphere.process.ProcessExecutor; import org.junit.jupiter.api.Disabled; import java.io.File; @@ -32,6 +33,7 @@ import static io.microsphere.management.JmxUtils.getRuntimeMXBean; import static io.microsphere.reflect.TypeUtils.asClass; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; +import static io.microsphere.util.SystemUtils.IS_OS_WINDOWS; import static io.microsphere.util.SystemUtils.JAVA_IO_TMPDIR; import static java.lang.String.valueOf; import static java.util.Collections.emptyList; @@ -178,6 +180,24 @@ protected File newTempFile(String path) { return new File(TEST_TEMP_DIR, path); } + protected File makeLinkFile(File targetFile) throws IOException { + File tempDir = createRandomTempDirectory(); + File linkFile = new File(tempDir, "link"); + boolean directory = targetFile.isDirectory(); + String targetPatth = targetFile.getAbsolutePath(); + String linkPath = linkFile.getAbsolutePath(); + final ProcessExecutor processExecutor; + if (IS_OS_WINDOWS) { + processExecutor = directory ? + new ProcessExecutor("mklink", "/D", targetPatth, linkPath) : + new ProcessExecutor("mklink", targetPatth, linkPath); + } else { + processExecutor = new ProcessExecutor("ln", "-s", targetPatth, linkPath); + } + processExecutor.execute(System.out); + return linkFile; + } + protected void assertThrowable(ThrowableAction action, Consumer failureHandler) { Throwable failure = null; try { diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index 83e81f5b5..b95091019 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -2,7 +2,6 @@ import io.microsphere.AbstractTestCase; import io.microsphere.lang.function.ThrowableConsumer; -import io.microsphere.process.ProcessExecutor; import org.junit.jupiter.api.Test; import java.io.File; @@ -33,7 +32,6 @@ import static io.microsphere.util.ClassLoaderUtils.getResource; import static io.microsphere.util.ExceptionUtils.wrap; import static io.microsphere.util.StringUtils.EMPTY_STRING; -import static io.microsphere.util.SystemUtils.IS_OS_WINDOWS; import static io.microsphere.util.SystemUtils.JAVA_IO_TMPDIR; import static java.lang.Thread.sleep; import static java.util.concurrent.Executors.newFixedThreadPool; @@ -305,24 +303,6 @@ void testGetCanonicalFile() { assertEquals(getCanonicalFile(tempFile), getCanonicalFile(getCanonicalFile(tempFile))); } - File makeLinkFile(File targetFile) throws IOException { - File tempDir = createRandomTempDirectory(); - File linkFile = new File(tempDir, "link"); - boolean directory = targetFile.isDirectory(); - String targetPatth = targetFile.getAbsolutePath(); - String linkPath = linkFile.getAbsolutePath(); - final ProcessExecutor processExecutor; - if (IS_OS_WINDOWS) { - processExecutor = directory ? - new ProcessExecutor("mklink", "/D", targetPatth, linkPath) : - new ProcessExecutor("mklink", targetPatth, linkPath); - } else { - processExecutor = new ProcessExecutor("ln", "-s", targetPatth, linkPath); - } - processExecutor.execute(System.out); - return linkFile; - } - void handleDirectoryOnIOException(ThrowableConsumer directoryHandler) throws Exception { ExecutorService executor = newSingleThreadExecutor(); executor.submit(() -> handleDirectoryOnIOException0(directoryHandler)); From 40f58da9afbdf0cf0281c3b85326888a9267152c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 15:07:02 +0800 Subject: [PATCH 106/438] Enhance ClassUtils behavior and tests Handle jar:// protocol and improve class-path resolution; simplify file vs jar detection and improve trace logging. Optimize jar scanning by pre-sizing collections and adjust exception logging. Refactor simple name resolution (extract getSimpleName overload) and make isAsciiDigit package-visible; update getClass to return Class objects unchanged. Update javadoc reference to FileUtils#resolveRelativePath. Add comprehensive tests for class-path/jar edge cases, simple-name/malformed names, ASCII digit checks, and other utilities, and include two empty .sar test resources. --- .../java/io/microsphere/util/ClassUtils.java | 68 ++++++----- .../io/microsphere/util/ClassUtilsTest.java | 114 +++++++++++++++++- .../resources/META-INF/empty-class-name.sar | Bin 0 -> 410 bytes .../src/test/resources/META-INF/empty.sar | Bin 0 -> 410 bytes 4 files changed, 150 insertions(+), 32 deletions(-) create mode 100644 microsphere-java-core/src/test/resources/META-INF/empty-class-name.sar create mode 100644 microsphere-java-core/src/test/resources/META-INF/empty.sar diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java index 45f2e42e5..046e6b572 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java @@ -7,6 +7,7 @@ import io.microsphere.annotation.Nonnull; import io.microsphere.annotation.Nullable; import io.microsphere.filter.ClassFileJarEntryFilter; +import io.microsphere.io.FileUtils; import io.microsphere.io.filter.FileExtensionFilter; import io.microsphere.io.filter.IOFileFilter; import io.microsphere.io.scanner.SimpleFileScanner; @@ -33,6 +34,7 @@ import java.util.jar.JarFile; import static io.microsphere.collection.CollectionUtils.isEmpty; +import static io.microsphere.collection.CollectionUtils.size; import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; import static io.microsphere.collection.SetUtils.newFixedLinkedHashSet; import static io.microsphere.collection.SetUtils.newLinkedHashSet; @@ -42,6 +44,7 @@ import static io.microsphere.constants.FileConstants.JAR_EXTENSION; import static io.microsphere.constants.PathConstants.SLASH; import static io.microsphere.constants.ProtocolConstants.FILE_PROTOCOL; +import static io.microsphere.constants.ProtocolConstants.JAR_PROTOCOL; import static io.microsphere.constants.SeparatorConstants.ARCHIVE_ENTRY_SEPARATOR; import static io.microsphere.constants.SymbolConstants.COLON_CHAR; import static io.microsphere.constants.SymbolConstants.DOLLAR_CHAR; @@ -1062,12 +1065,19 @@ public static String resolvePackageName(@Nullable String className) { @Immutable public static Set findClassNamesInClassPath(@Nullable String classPath, boolean recursive) { String protocol = resolveProtocol(classPath); - final String resolvedClassPath; - if (FILE_PROTOCOL.equals(protocol)) { + String resolvedClassPath = classPath; + + if (JAR_PROTOCOL.equals(protocol)) { + String path = substringAfter(classPath, protocol + COLON_CHAR); + return findClassNamesInClassPath(path, recursive); + } else if (FILE_PROTOCOL.equals(protocol)) { + String prefix = protocol + COLON_CHAR; resolvedClassPath = substringBetween(classPath, protocol + COLON_CHAR, ARCHIVE_ENTRY_SEPARATOR); - } else { - resolvedClassPath = classPath; + if (resolvedClassPath == null) { + resolvedClassPath = substringAfter(classPath, prefix); + } } + File classesFileHolder = new File(resolvedClassPath); // File or Directory return findClassNamesInClassPath(classesFileHolder, recursive); } @@ -1105,17 +1115,12 @@ public static Set findClassNamesInClassPath(@Nullable File classPath, bo Set classNames = emptySet(); if (classPath.isDirectory()) { // Directory classNames = findClassNamesInDirectory(classPath, recursive); - } else if (classPath.isFile()) { // File - if (JAR_FILE_EXTENSION_FILTER.accept(classPath)) { // JarFile - classNames = findClassNamesInJarFile(classPath, recursive); - } + } else if (JAR_FILE_EXTENSION_FILTER.accept(classPath)) { // JarFile + classNames = findClassNamesInJarFile(classPath, recursive); } - if (isEmpty(classNames)) { - if (logger.isTraceEnabled()) { - logger.trace("No Class was found in the classPath : '{}' , recursive : {}", classPath, recursive); - } - } + logger.trace("To find the class names in the class path['{}' , recursive : {}] : {}", classPath, recursive, classNames); + return classNames; } @@ -1197,10 +1202,11 @@ public static Set findClassNamesInJarFile(@Nullable File jarFile, boolea try { JarFile jarFile_ = new JarFile(jarFile); Set jarEntries = INSTANCE.scan(jarFile_, recursive, ClassFileJarEntryFilter.INSTANCE); - if (isEmpty(jarEntries)) { + int size = size(jarEntries); + if (size == 0) { classNames = emptySet(); } else { - classNames = newLinkedHashSet(); + classNames = newLinkedHashSet(size); for (JarEntry jarEntry : jarEntries) { String jarEntryName = jarEntry.getName(); String className = resolveClassName(jarEntryName); @@ -1212,10 +1218,8 @@ public static Set findClassNamesInJarFile(@Nullable File jarFile, boolea } } catch (Exception e) { classNames = emptySet(); - if (logger.isTraceEnabled()) { - logger.trace("The class names can't be resolved by SimpleJarEntryScanner#scan(jarFile = {} ," + - " recursive = {} , jarEntryFilter = ClassFileJarEntryFilter)", jarFile, recursive, e); - } + logger.trace("The class names can't be resolved by SimpleJarEntryScanner#scan(jarFile = {} ," + + " recursive = {} , jarEntryFilter = ClassFileJarEntryFilter)", jarFile, recursive, e); } return classNames; } @@ -1239,7 +1243,7 @@ public static Set findClassNamesInJarFile(@Nullable File jarFile, boolea * @param classesDirectory the root directory containing compiled classes, must not be {@code null} * @param classFile the class file to resolve the name for, must not be {@code null} * @return the fully qualified class name, never {@code null} - * @see #resolveRelativePath(File, File) + * @see FileUtils#resolveRelativePath(File, File) * @see #resolveClassName(String) */ protected static String resolveClassName(File classesDirectory, File classFile) { @@ -1714,7 +1718,10 @@ public static Class getType(@Nullable Object value) { */ @Nullable public static Class getClass(@Nullable Object object) { - return object == null ? null : object.getClass(); + if (object == null) { + return null; + } + return object instanceof Class ? (Class) object : object.getClass(); } /** @@ -1862,7 +1869,11 @@ private static String getSimpleName(Class type, boolean array) { if (array) { return getSimpleName(type.getComponentType()) + "[]"; } - String simpleName = type.getName(); + return getSimpleName(type, type.getName()); + } + + static String getSimpleName(Class type, String className) { + String simpleName = className; Class enclosingClass = type.getEnclosingClass(); if (enclosingClass == null) { // top level class simpleName = simpleName.substring(simpleName.lastIndexOf(DOT_CHAR) + 1); @@ -1871,16 +1882,20 @@ private static String getSimpleName(Class type, boolean array) { simpleName = simpleName.substring(ecName.length()); // Remove leading "\$[0-9]*" from the name int length = simpleName.length(); - if (length < 1 || simpleName.charAt(0) != DOLLAR_CHAR) throw new InternalError("Malformed class name"); + if (length < 1 || simpleName.charAt(0) != DOLLAR_CHAR) { + throw new InternalError("Malformed class name"); + } int index = 1; - while (index < length && isAsciiDigit(simpleName.charAt(index))) index++; + while (index < length && isAsciiDigit(simpleName.charAt(index))) { + index++; + } // Eventually, this is the empty string iff this is an anonymous class return simpleName.substring(index); } return simpleName; } - private static boolean isAsciiDigit(char c) { + static boolean isAsciiDigit(char c) { return '0' <= c && c <= '9'; } @@ -2218,5 +2233,4 @@ public static URL getCodeSourceLocation(@Nullable Class type) { private ClassUtils() { } - -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java index 30addf6f1..510a75501 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java @@ -5,14 +5,17 @@ import io.microsphere.AbstractTestCase; import io.microsphere.lang.ClassDataRepository; +import io.microsphere.test.A; import org.junit.jupiter.api.Test; import javax.annotation.Nonnull; import java.io.File; +import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URL; import java.nio.CharBuffer; import java.util.AbstractCollection; import java.util.AbstractMap; @@ -26,7 +29,9 @@ import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; +import java.util.function.Predicate; +import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.ofEntry; import static io.microsphere.constants.SymbolConstants.SPACE; import static io.microsphere.util.ArrayUtils.EMPTY_BOOLEAN_ARRAY; @@ -40,13 +45,17 @@ import static io.microsphere.util.ArrayUtils.EMPTY_OBJECT_ARRAY; import static io.microsphere.util.ArrayUtils.EMPTY_SHORT_ARRAY; import static io.microsphere.util.ArrayUtils.ofArray; +import static io.microsphere.util.ClassLoaderUtils.getResource; import static io.microsphere.util.ClassUtils.ARRAY_SUFFIX; import static io.microsphere.util.ClassUtils.PRIMITIVE_TYPES; import static io.microsphere.util.ClassUtils.arrayTypeEquals; import static io.microsphere.util.ClassUtils.cast; import static io.microsphere.util.ClassUtils.concreteClassCache; import static io.microsphere.util.ClassUtils.findAllClasses; +import static io.microsphere.util.ClassUtils.findClassNamesInClassPath; +import static io.microsphere.util.ClassUtils.findClassNamesInJarFile; import static io.microsphere.util.ClassUtils.getAllClasses; +import static io.microsphere.util.ClassUtils.getAllInheritedTypes; import static io.microsphere.util.ClassUtils.getAllInterfaces; import static io.microsphere.util.ClassUtils.getAllSuperClasses; import static io.microsphere.util.ClassUtils.getClasses; @@ -60,6 +69,7 @@ import static io.microsphere.util.ClassUtils.getTypes; import static io.microsphere.util.ClassUtils.isAbstractClass; import static io.microsphere.util.ClassUtils.isArray; +import static io.microsphere.util.ClassUtils.isAsciiDigit; import static io.microsphere.util.ClassUtils.isAssignableFrom; import static io.microsphere.util.ClassUtils.isCharSequence; import static io.microsphere.util.ClassUtils.isClass; @@ -80,6 +90,7 @@ import static io.microsphere.util.ClassUtils.resolvePrimitiveType; import static io.microsphere.util.ClassUtils.resolveWrapperType; import static io.microsphere.util.ClassUtils.tryResolveWrapperType; +import static java.lang.Integer.valueOf; import static java.lang.Thread.State.NEW; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; @@ -183,6 +194,9 @@ void testIsGeneralClass() { assertFalse(isGeneralClass(Override.class)); // test enum assertFalse(isGeneralClass(TimeUnit.class)); + // test synthetic + Predicate p = a -> true; + assertFalse(isGeneralClass(p.negate().getClass())); // test primitive assertFalse(isGeneralClass(int.class)); // test array @@ -444,6 +458,11 @@ void testIsWrapperType() { assertFalse(isWrapperType(null)); } + @Test + void testIsWrapperTypeWithObject() { + assertTrue(isWrapperType(valueOf(1))); + } + @Test void testArrayTypeEquals() { assertFalse(arrayTypeEquals(null, null)); @@ -495,9 +514,26 @@ void testResolvePackageNameOnPrimitiveType() { } @Test - void testFindClassNamesInClassPath() { + void testFindClassNamesInClassPathWithFilePath() throws IOException { assertFindClassNamesMethod(ClassUtilsTest.class, ClassUtils::findClassNamesInClassPath); assertFindClassNamesMethod(Nonnull.class, ClassUtils::findClassNamesInClassPath); + + File tempFile = createRandomTempFile(); + assertTrue(findClassNamesInClassPath(tempFile, true).isEmpty()); + assertTrue(findClassNamesInClassPath(tempFile, false).isEmpty()); + + + File link = makeLinkFile(tempFile); + assertTrue(findClassNamesInClassPath(link, false).isEmpty()); + } + + @Test + void testFindClassNamesInClassPathWithStringPath() { + URL location = getCodeSourceLocation(Nonnull.class); + assertFindClassNamesInClassPath(location); + + location = getResource("javax/annotation/Nonnull.class"); + assertFindClassNamesInClassPath(location); } @Test @@ -510,9 +546,31 @@ void testFindClassNamesInJarFile() { assertFindClassNamesMethod(Nonnull.class, ClassUtils::findClassNamesInJarFile); } + @Test + void testFindClassNamesInEmptyJarFile() { + URL resource = getResource("META-INF/empty.sar"); + File emptyJarFile = new File(resource.getFile()); + assertTrue(findClassNamesInJarFile(emptyJarFile, true).isEmpty()); + assertTrue(findClassNamesInJarFile(emptyJarFile, false).isEmpty()); + } + + @Test + void testFindClassNamesInEmptyClassNameJarFile() { + URL resource = getResource("META-INF/empty-class-name.sar"); + File emptyClassNameJarFile = new File(resource.getFile()); + assertTrue(findClassNamesInJarFile(emptyClassNameJarFile, true).isEmpty()); + assertTrue(findClassNamesInJarFile(emptyClassNameJarFile, false).isEmpty()); + } + @Test void testResolveClassName() { assertEquals("java.lang.String", resolveClassName("java/lang/String.class")); + assertEquals("java.lang.String", resolveClassName("/java/lang/String.class")); + } + + @Test + void testResolveClassNameOnNull() { + assertNull(resolveClassName(null)); } @Test @@ -541,6 +599,17 @@ void testGetAllInterfaces() { assertSame(emptyList(), getAllInterfaces(int.class)); } + @Test + void testGetAllInheritedTypes() { + List> types = getAllInheritedTypes(A.class); + assertEquals(ofList(Object.class, Serializable.class), types); + } + + @Test + void testGetAllInheritedTypesOnNull() { + assertEquals(emptyList(), getAllInheritedTypes(null)); + } + @Test void testGetAllClasses() { List> allClasses = getAllClasses(Object.class); @@ -701,6 +770,32 @@ class LocalClass { assertEquals("double[]", getSimpleName(double[].class)); } + @Test + void testGetSimpleNameOnNull() { + assertNull(getSimpleName(null)); + } + + @Test + void testGetSimpleNameOnMalformedClassName() { + assertThrows(InternalError.class, () -> getSimpleName(Thread.State.class, "java.lang.Thread")); + assertThrows(InternalError.class, () -> getSimpleName(Thread.State.class, "java.lang.Thread.")); + } + + @Test + void testIsAsciiDigit() { + for (int i = 0; i <= 9; i++) { + assertTrue(isAsciiDigit((char) ('0' + i))); + } + + for (int i = 1; i <= 9; i++) { + assertFalse(isAsciiDigit((char) ('0' - i))); + } + + for (int i = 1; i <= 9; i++) { + assertFalse(isAsciiDigit((char) ('9' + i))); + } + } + @Test void testGetTopComponentType() { assertNull(getTopComponentType((Object) null)); @@ -728,14 +823,17 @@ void testIsAssignableFrom() { void testGetType() { assertNull(getType(null)); assertSame(String.class, getType("")); - assertSame(Class.class, getType(String.class)); + assertSame(String.class, getType(String.class)); + assertSame(Class.class, getType(Class.class)); + } @Test void testGetClass() { assertNull(ClassUtils.getClass(null)); assertSame(String.class, ClassUtils.getClass("")); - assertSame(Class.class, ClassUtils.getClass(String.class)); + assertSame(String.class, ClassUtils.getClass(String.class)); + assertSame(Class.class, ClassUtils.getClass(Class.class)); } @Test @@ -744,7 +842,7 @@ void testGetTypes() { assertSame(EMPTY_CLASS_ARRAY, getTypes()); assertSame(EMPTY_CLASS_ARRAY, getTypes(EMPTY_OBJECT_ARRAY)); - assertArrayEquals(ofArray(String.class, Integer.class), getTypes("", Integer.valueOf((1)))); + assertArrayEquals(ofArray(String.class, Integer.class), getTypes("", valueOf((1)))); } @Test @@ -753,7 +851,7 @@ void testGetClasses() { assertSame(EMPTY_CLASS_ARRAY, getClasses()); assertSame(EMPTY_CLASS_ARRAY, getClasses(EMPTY_OBJECT_ARRAY)); - assertArrayEquals(ofArray(String.class, Integer.class), getClasses("", Integer.valueOf((1)))); + assertArrayEquals(ofArray(String.class, Integer.class), getClasses("", valueOf((1)))); } @Test @@ -769,6 +867,7 @@ void testIsDerived() { @Test void testNewInstance() { assertEquals("test", newInstance(String.class, "test")); + assertEquals(valueOf(1), newInstance(Integer.class, valueOf(1))); assertThrows(IllegalArgumentException.class, () -> newInstance(String.class, 1)); } @@ -818,5 +917,10 @@ void testGetCodeSourceLocation() { assertFalse(findClassNamesFunction.apply(new File(path), true).isEmpty()); } + private void assertFindClassNamesInClassPath(URL location) { + String path = location.toString(); + Set classNamesInClassPath = findClassNamesInClassPath(path, true); + assertFalse(classNamesInClassPath.isEmpty()); + } } diff --git a/microsphere-java-core/src/test/resources/META-INF/empty-class-name.sar b/microsphere-java-core/src/test/resources/META-INF/empty-class-name.sar new file mode 100644 index 0000000000000000000000000000000000000000..42299b6d4e52fd63539d89fbdffe077809c21500 GIT binary patch literal 410 zcmWIWW@h1HVBlb2IA7`+!+-=h8CV#6T|*poJ^kGD|D9rB2mmS-Vc_84z)&gz)CO1T z>*(j{<{BKL=j-;__snS@Z(Y5MyxzK6=gyqp9At3C_`%a6JuhD!Pv48BtF{CgFm=6{ zsgdzRTvhG)5-p48;-XK)Ri`nCW`8RBSi}gllbz$4{k*MCKpQ|9n?2Qpy} SW(B#Pff)!l0_n>j4g&y|A5DS) literal 0 HcmV?d00001 diff --git a/microsphere-java-core/src/test/resources/META-INF/empty.sar b/microsphere-java-core/src/test/resources/META-INF/empty.sar new file mode 100644 index 0000000000000000000000000000000000000000..55d3c3e3cf93ae5193ab9b789d709d196995ca64 GIT binary patch literal 410 zcmWIWW@h1HVBlb2=+5(uVL$?$3@i-3t|5-Po_=on|4uP51OSzaFmP~iU?>#Nf%Ih%hXDYfP)ybU literal 0 HcmV?d00001 From c23c11f52cc38fb5eb4235daf589f85680cd9e13 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 15:07:40 +0800 Subject: [PATCH 107/438] Refactor PriorityComparator and add tests Make PriorityComparator more testable and flexible: use ClassUtils.getType to derive types, add a compare overload that accepts the priority annotation class, and expose getValue as package-visible static. Remove the private asClass helper and delegate annotation lookup to the provided priority class. Update unit tests to statically import and exercise the new compare overload, add cases for null priorityClass and null types, and rename an existing test for clarity. These changes improve testability and handling of null/undefined priority scenarios. --- .../microsphere/util/PriorityComparator.java | 25 ++++++++++--------- .../util/PriorityComparatorTest.java | 13 +++++++++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/PriorityComparator.java b/microsphere-java-core/src/main/java/io/microsphere/util/PriorityComparator.java index 6b7c6d062..f0ab08fdd 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/PriorityComparator.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/PriorityComparator.java @@ -16,12 +16,14 @@ */ package io.microsphere.util; +import java.lang.annotation.Annotation; import java.util.Comparator; import java.util.Objects; import static io.microsphere.reflect.MethodUtils.invokeMethod; import static io.microsphere.util.AnnotationUtils.findAnnotation; import static io.microsphere.util.ClassLoaderUtils.resolveClass; +import static io.microsphere.util.ClassUtils.getType; /** * A {@link Comparator} implementation that sorts objects based on the value of the @@ -59,16 +61,20 @@ public class PriorityComparator implements Comparator { @Override public int compare(Object o1, Object o2) { - return compare(asClass(o1), asClass(o2)); + return compare(getType(o1), getType(o2)); } - public static int compare(Class type1, Class type2) { - if (Objects.equals(type1, type2) || PRIORITY_CLASS == null) { + static int compare(Class type1, Class type2) { + return compare(type1, type2, PRIORITY_CLASS); + } + + static int compare(Class type1, Class type2, Class priorityClass) { + if (Objects.equals(type1, type2) || priorityClass == null) { return 0; } - Object priority1 = findAnnotation(type1, PRIORITY_CLASS); - Object priority2 = findAnnotation(type2, PRIORITY_CLASS); + Object priority1 = findAnnotation(type1, priorityClass); + Object priority2 = findAnnotation(type2, priorityClass); int priorityValue1 = getValue(priority1); int priorityValue2 = getValue(priority2); @@ -76,13 +82,8 @@ public static int compare(Class type1, Class type2) { return Integer.compare(priorityValue1, priorityValue2); } - private static Class asClass(Object object) { - return object instanceof Class ? (Class) object : object.getClass(); - } - - private static int getValue(Object priority) { + static int getValue(Object priority) { int value = priority == null ? UNDEFINED_VALUE : invokeMethod(priority, "value"); return value < 0 ? UNDEFINED_VALUE : value; } - -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/PriorityComparatorTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/PriorityComparatorTest.java index bfedf9031..92835628b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/PriorityComparatorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/PriorityComparatorTest.java @@ -20,6 +20,7 @@ import javax.annotation.Priority; +import static io.microsphere.util.PriorityComparator.compare; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -33,7 +34,7 @@ class PriorityComparatorTest { @Test - void test() { + void testCompileWithObject() { PriorityComparator comparator = PriorityComparator.INSTANCE; assertEquals(0, comparator.compare(new NoPriority(), new NoPriority())); assertEquals(0, comparator.compare(new PriorityOneType(), new PriorityOneType())); @@ -43,6 +44,16 @@ void test() { assertEquals(1, comparator.compare(new PriorityTwoType(), new PriorityOneType())); } + @Test + void testCompileWithSameTypes() { + assertEquals(0, compare(null, null, null)); + } + + @Test + void testCompileWithNullPriorityClass() { + assertEquals(0, compare(Object.class, String.class, null)); + } + static class NoPriority { } From 85d59d91a567d41627c1800973739ad9cd9f5f10 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 15:14:16 +0800 Subject: [PATCH 108/438] Remove try-catch from array type name code Remove an unnecessary try-catch block in the array type name construction. The method now iterates to the component type and appends the appropriate number of "[]" suffixes directly, simplifying control flow and reducing code size without changing behavior. --- .../java/io/microsphere/util/ClassUtils.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java index 046e6b572..53a1430bf 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java @@ -1811,22 +1811,19 @@ public static String getTypeName(@Nullable Class type) { return null; } if (type.isArray()) { - try { - Class cl = type; - int dimensions = 0; - while (cl.isArray()) { - dimensions++; - cl = cl.getComponentType(); - } - String name = getTypeName(cl); - StringBuilder sb = new StringBuilder(name.length() + dimensions * 2); - sb.append(name); - for (int i = 0; i < dimensions; i++) { - sb.append("[]"); - } - return sb.toString(); - } catch (Throwable e) { + Class cl = type; + int dimensions = 0; + while (cl.isArray()) { + dimensions++; + cl = cl.getComponentType(); + } + String name = getTypeName(cl); + StringBuilder sb = new StringBuilder(name.length() + dimensions * 2); + sb.append(name); + for (int i = 0; i < dimensions; i++) { + sb.append("[]"); } + return sb.toString(); } return type.getName(); } From 42a95f5cac39d52787be591ee0c48e493f6e7fa0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 15:30:23 +0800 Subject: [PATCH 109/438] Remove redundant null initialization Eliminate the unnecessary initialization of the local variable `klass` in ClassLoaderUtils (declare Class klass without assigning null). This is a no-op behavior change that cleans up the code and removes a redundant assignment (addresses style/lint warnings). --- .../src/main/java/io/microsphere/util/ClassLoaderUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java index 78a6a4f80..4bb15f331 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java @@ -562,7 +562,7 @@ protected static Class doLoadClass(ClassLoader classLoader, String className) if (isBlank(className)) { return null; } - Class klass = null; + Class klass; try { klass = classLoader.loadClass(className); } catch (Throwable e) { From 5c2992dd4db81644a6566c63e977db1d2832c8d4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 15:30:33 +0800 Subject: [PATCH 110/438] Refactor URLStreamHandlerFactory and add test Refactor StandardURLStreamHandlerFactory to use utility helpers (FieldUtils.getStaticFieldValue, ClassLoaderUtils.resolveClass, ClassUtils.newInstance) instead of low-level reflection and Class.forName. Simplify handler creation flow by delegating to a new overloaded method and removing trySetAccessible/explicit exception swallowing. Add StandardURLStreamHandlerFactoryTest with basic assertions for creating the 'file' handler to increase test coverage. --- .../net/StandardURLStreamHandlerFactory.java | 38 ++++++--------- .../StandardURLStreamHandlerFactoryTest.java | 47 +++++++++++++++++++ 2 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/net/StandardURLStreamHandlerFactoryTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java index 7656ad31a..29e2c350e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java @@ -26,9 +26,10 @@ import static io.microsphere.constants.SymbolConstants.DOT; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.net.URLUtils.DEFAULT_HANDLER_PACKAGE_PREFIX; -import static io.microsphere.reflect.AccessibleObjectUtils.trySetAccessible; import static io.microsphere.reflect.FieldUtils.findField; -import static java.lang.Class.forName; +import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; +import static io.microsphere.util.ClassLoaderUtils.resolveClass; +import static io.microsphere.util.ClassUtils.newInstance; /** * Standard implementation of {@link URLStreamHandlerFactory} that creates a new instance of @@ -68,35 +69,26 @@ public class StandardURLStreamHandlerFactory implements URLStreamHandlerFactory @Override public URLStreamHandler createURLStreamHandler(String protocol) { - URLStreamHandler handler = createURLStreamHandlerFromDefaultFactory(protocol); + return createURLStreamHandler(defaultFactoryField, protocol); + } + + URLStreamHandler createURLStreamHandler(Field defaultFactoryField, String protocol) { + URLStreamHandler handler = createURLStreamHandlerFromDefaultFactory(defaultFactoryField, protocol); if (handler == null) { // <= JDK 8 works String name = DEFAULT_HANDLER_PACKAGE_PREFIX + DOT + protocol + DOT + "Handler"; - try { - Object o = forName(name).newInstance(); - return (URLStreamHandler) o; - } catch (Exception x) { - // For compatibility, all Exceptions are ignored. - // any number of exceptions can get thrown here - } + Class handlerClass = resolveClass(name); + return (URLStreamHandler) newInstance(handlerClass); } return handler; } - URLStreamHandler createURLStreamHandlerFromDefaultFactory(String protocol) { + URLStreamHandler createURLStreamHandlerFromDefaultFactory(Field defaultFactoryField, String protocol) { if (defaultFactoryField == null) { - if (logger.isTraceEnabled()) { - logger.trace("The 'defaultFactory' field can't be found in the class URL."); - } + logger.trace("The 'defaultFactory' field can't be found in the class URL."); return null; } - URLStreamHandler handler = null; - try { - trySetAccessible(defaultFactoryField); - URLStreamHandlerFactory factory = (URLStreamHandlerFactory) defaultFactoryField.get(null); - handler = factory.createURLStreamHandler(protocol); - } catch (Exception e) { - // ignore - } + URLStreamHandlerFactory factory = getStaticFieldValue(defaultFactoryField); + URLStreamHandler handler = factory.createURLStreamHandler(protocol); return handler; } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/StandardURLStreamHandlerFactoryTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/StandardURLStreamHandlerFactoryTest.java new file mode 100644 index 000000000..527850016 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/net/StandardURLStreamHandlerFactoryTest.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package io.microsphere.net; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * {@link StandardURLStreamHandlerFactory} Test + * + * @author Mercy + * @see StandardURLStreamHandlerFactory + * @since 1.0.0 + */ +class StandardURLStreamHandlerFactoryTest { + + private StandardURLStreamHandlerFactory factory; + + @BeforeEach + void setUp() { + this.factory = new StandardURLStreamHandlerFactory(); + } + + @Test + void testCreateURLStreamHandler() { + assertNotNull(this.factory.createURLStreamHandler("file")); + assertNotNull(this.factory.createURLStreamHandler(null, "file")); + } +} \ No newline at end of file From 8c6dc72d1efcde483175b2ab361f50f66c00a4fa Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 15:36:52 +0800 Subject: [PATCH 111/438] Null-check handlerClass before instantiation Ensure handlerClass is non-null before calling newInstance to avoid a NullPointerException when resolveClass fails to find the handler class. This prevents attempting to instantiate a null handler and keeps behavior consistent when no default handler is available. --- .../io/microsphere/net/StandardURLStreamHandlerFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java index 29e2c350e..9a23d0c79 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java @@ -77,7 +77,9 @@ URLStreamHandler createURLStreamHandler(Field defaultFactoryField, String protoc if (handler == null) { // <= JDK 8 works String name = DEFAULT_HANDLER_PACKAGE_PREFIX + DOT + protocol + DOT + "Handler"; Class handlerClass = resolveClass(name); - return (URLStreamHandler) newInstance(handlerClass); + if (handlerClass != null) { + handler = (URLStreamHandler) newInstance(handlerClass); + } } return handler; } From 41c7ba18ca8dd78a6e8e68b8b89d65e3d3e2698b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 17:30:53 +0800 Subject: [PATCH 112/438] Remove logger.isTraceEnabled() guard Call logger.trace unconditionally in IOUtils.copy by removing the surrounding isTraceEnabled() check. This simplifies the code and delegates enabled-level checks to the logging implementation. Affects microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java. --- .../src/main/java/io/microsphere/io/IOUtils.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java b/microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java index 424454bff..c5bd0b47e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java @@ -367,9 +367,7 @@ public static int copy(InputStream in, OutputStream out) throws IOException { byteCount += bytesRead; } out.flush(); - if (logger.isTraceEnabled()) { - logger.trace("Copied {} bytes[buffer size : {}] from InputStream[{}] to OutputStream[{}]", byteCount, BUFFER_SIZE, in, out); - } + logger.trace("Copied {} bytes[buffer size : {}] from InputStream[{}] to OutputStream[{}]", byteCount, BUFFER_SIZE, in, out); return byteCount; } From 993983c68bbe9b7d95f8d86510047f4f22301301 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 17:31:25 +0800 Subject: [PATCH 113/438] Use ExecutorService and timeout in ProcessExecutor Refactor ProcessExecutor to run process I/O in a single-threaded ExecutorService with proper timeout handling and improved I/O buffering. Adds a configurable PROCESS_EXECUTION_TIMEOUT_PROPERTY_NAME (default 30000ms), DEFAULT_TIMEOUT, wrapping of execution into a Future, and uses FastByteArray streams + IO copy utilities. Removes the finished flag and isFinished API usage; execute now supports explicit TimeUnit and propagates TimeoutException and wrapped IOExceptions. Tests were updated to reflect the new API and behavior (method signatures changed to throw Exception, removed isFinished assertions, and added tests for not-found commands). Logging and graceful executor shutdown on JVM exit were also added. --- .../microsphere/process/ProcessExecutor.java | 151 ++++++++++-------- .../java/io/microsphere/AbstractTestCase.java | 2 +- .../java/io/microsphere/io/FileUtilsTest.java | 6 +- .../process/ProcessExecutorTest.java | 23 ++- .../process/ProcessManagerTest.java | 4 +- .../io/microsphere/util/ClassUtilsTest.java | 3 +- 6 files changed, 106 insertions(+), 83 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java index f74674d7e..4e0d7af2e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java @@ -1,18 +1,33 @@ package io.microsphere.process; +import io.microsphere.annotation.ConfigurationProperty; +import io.microsphere.io.FastByteArrayInputStream; +import io.microsphere.io.FastByteArrayOutputStream; +import io.microsphere.logging.Logger; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static io.microsphere.annotation.ConfigurationProperty.SYSTEM_PROPERTIES_SOURCE; +import static io.microsphere.concurrent.CustomizedThreadFactory.newThreadFactory; +import static io.microsphere.concurrent.ExecutorUtils.shutdownOnExit; import static io.microsphere.constants.SymbolConstants.SPACE_CHAR; +import static io.microsphere.io.IOUtils.copy; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.process.ProcessManager.INSTANCE; -import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.ArrayUtils.isNotEmpty; -import static java.lang.Long.MAX_VALUE; +import static io.microsphere.util.ExceptionUtils.wrap; import static java.lang.Long.getLong; +import static java.lang.Long.parseLong; import static java.lang.Runtime.getRuntime; -import static java.lang.System.currentTimeMillis; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; /** * {@link Process} Executor @@ -43,7 +58,37 @@ */ public class ProcessExecutor { - private static final long waitForTimeInSecond = getLong("process.executor.wait.for", 1); + private static final Logger logger = getLogger(ProcessExecutor.class); + + private static final ExecutorService executor = newSingleThreadExecutor(newThreadFactory("process-exec", true)); + + /** + * The default proeprty value for the timeout of process execution : 30 seconds + */ + public static final String DEFAULT_PROCESS_EXECUTION_TIMEOUT_PROPERTY_VAUE = "30000"; + + /** + * The default value for the timeout of process execution : 30 seconds + */ + public static final long DEFAULT_PROCESS_EXECUTION_TIMEOUT = parseLong(DEFAULT_PROCESS_EXECUTION_TIMEOUT_PROPERTY_VAUE); + + /** + * The property name for the timeout of process execution : "process.execution.timeout" + * + * @see #DEFAULT_PROCESS_EXECUTION_TIMEOUT_PROPERTY_VAUE + * @see #DEFAULT_PROCESS_EXECUTION_TIMEOUT + */ + @ConfigurationProperty( + type = long.class, + defaultValue = DEFAULT_PROCESS_EXECUTION_TIMEOUT_PROPERTY_VAUE, + source = SYSTEM_PROPERTIES_SOURCE + ) + public static final String PROCESS_EXECUTION_TIMEOUT_PROPERTY_NAME = "process.execution.timeout"; + + /** + * the timeout of process execution + */ + public static final long DEFAULT_TIMEOUT = getLong(PROCESS_EXECUTION_TIMEOUT_PROPERTY_NAME, DEFAULT_PROCESS_EXECUTION_TIMEOUT); private final ProcessManager processManager = INSTANCE; @@ -53,7 +98,9 @@ public class ProcessExecutor { private final String options; - private boolean finished; + static { + shutdownOnExit(executor); + } /** * Constructor @@ -74,23 +121,17 @@ public ProcessExecutor(String command, String... options) { /** * Execute current process. - *

    - * // * @param inputStream input stream keeps output stream from process * * @param outputStream output stream for process normal or error input stream. - * @throws IOException if process execution is failed. + * @throws IOException if process execution is failed. + * @throws TimeoutException if the execution is timeout over specified {@link #DEFAULT_TIMEOUT} */ - public void execute(OutputStream outputStream) throws IOException { - try { - this.execute(outputStream, MAX_VALUE); - } catch (TimeoutException e) { - } + public void execute(OutputStream outputStream) throws IOException, TimeoutException { + this.execute(outputStream, DEFAULT_TIMEOUT); } /** * Execute current process. - *

    - * // * @param inputStream input stream keeps output stream from process * * @param outputStream output stream for process normal or error input stream. * @param timeoutInMilliseconds milliseconds timeout @@ -98,64 +139,48 @@ public void execute(OutputStream outputStream) throws IOException { * @throws TimeoutException if the execution is timeout over specified timeoutInMilliseconds */ public void execute(OutputStream outputStream, long timeoutInMilliseconds) throws IOException, TimeoutException { - Process process = runtime.exec(commandLine); - long startTime = currentTimeMillis(); - long endTime = -1L; - InputStream processInputStream = process.getInputStream(); - InputStream processErrorInputStream = process.getErrorStream(); -// OutputStream processOutputStream = process.getOutputStream(); - int exitValue = -1; - while (!finished) { - long costTime = endTime - startTime; - if (costTime > timeoutInMilliseconds) { - finished = true; - processManager.destroy(process); - String message = format("Execution is timeout[{} ms]!", timeoutInMilliseconds); - throw new TimeoutException(message); - } + execute(outputStream, timeoutInMilliseconds, MILLISECONDS); + } + + /** + * Execute current process. + * + * @param outputStream output stream for process normal or error input stream. + * @param timeout the timeout value + * @param timeUnit {@link TimeUnit} + * @throws IOException if process execution is failed. + * @throws TimeoutException if the execution is timeout over specified timeout and timeUnit + */ + public void execute(OutputStream outputStream, long timeout, TimeUnit timeUnit) throws IOException, TimeoutException { + + Future future = executor.submit(() -> { + Process process = runtime.exec(commandLine); + InputStream processInputStream = process.getInputStream(); + InputStream processErrorInputStream = process.getErrorStream(); + FastByteArrayOutputStream targetOutputStream = new FastByteArrayOutputStream(); + int exitValue = -1; try { processManager.addUnfinishedProcess(process, options); - while (processInputStream.available() > 0) { - outputStream.write(processInputStream.read()); - } - while (processErrorInputStream.available() > 0) { - outputStream.write(processErrorInputStream.read()); - } + // Copy the standard input stream + copy(processInputStream, targetOutputStream); + // Copy the error input stream + copy(processErrorInputStream, targetOutputStream); exitValue = process.exitValue(); if (exitValue != 0) { throw new IOException(); } - finished = true; - } catch (IllegalThreadStateException e) { - // Process is not finished yet; - // Sleep a little to save on CPU cycles - waitFor(waitForTimeInSecond); - endTime = currentTimeMillis(); } finally { processManager.removeUnfinishedProcess(process, options); + logger.trace("The command['{}'] is executed with exit value : {}", commandLine, exitValue); } - } - } + return targetOutputStream.toByteArray(); + }); - /** - * Wait for specified seconds - * - * @param seconds specified seconds - */ - private void waitFor(long seconds) { try { - Thread.sleep(seconds * 1000); - } catch (InterruptedException e) { - Thread.interrupted(); + byte[] bytes = future.get(timeout, timeUnit); + copy(new FastByteArrayInputStream(bytes), outputStream); + } catch (InterruptedException | ExecutionException e) { + throw wrap(e, IOException.class); } } - - /** - * Check current process finish or not. - * - * @return true if current process finished - */ - public boolean isFinished() { - return finished; - } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java index 048288427..9f8e167a8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java +++ b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java @@ -180,7 +180,7 @@ protected File newTempFile(String path) { return new File(TEST_TEMP_DIR, path); } - protected File makeLinkFile(File targetFile) throws IOException { + protected File makeLinkFile(File targetFile) throws Exception { File tempDir = createRandomTempDirectory(); File linkFile = new File(tempDir, "link"); boolean directory = targetFile.isDirectory(); diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index b95091019..5980784f5 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -121,7 +121,7 @@ void testDeleteDirectoryOnIOException() throws Exception { } @Test - void testDeleteDirectoryOnLink() throws IOException { + void testDeleteDirectoryOnLink() throws Exception { File testDir = createRandomTempDirectory(); File linkDir = makeLinkFile(testDir); assertEquals(1, deleteDirectory(linkDir)); @@ -263,7 +263,7 @@ void testForceDeleteOnExit() throws IOException { } @Test - void testDeleteDirectoryOnExit() throws IOException { + void testDeleteDirectoryOnExit() throws Exception { File tempDir = createRandomTempDirectory(); File link = makeLinkFile(tempDir); @@ -285,7 +285,7 @@ void testDeleteDirectoryOnExitOnNotExists() throws IOException { } @Test - void testIsSymlink() throws IOException { + void testIsSymlink() throws Exception { File tempDir = createRandomTempDirectory(); File targetFile = createRandomFile(tempDir); File linkFile = makeLinkFile(targetFile); diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java index ca4892f90..bd7abe382 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java @@ -6,10 +6,10 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -29,17 +29,11 @@ void setUp() { this.executor = new ProcessExecutor("java", "-version"); } - @Test - void testIsFinished() throws Exception { - assertFalse(this.executor.isFinished()); - } - @Test void testExecute() throws Exception { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8 * 1024); this.executor.execute(outputStream); assertTrue(outputStream.size() > 0); - assertTrue(this.executor.isFinished()); String response = new String(outputStream.toByteArray()); log(response); } @@ -47,17 +41,22 @@ void testExecute() throws Exception { @Test void testExecuteWithTimeout() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8 * 1024); - assertThrows(TimeoutException.class, () -> this.executor.execute(outputStream, 1)); + assertThrows(TimeoutException.class, () -> this.executor.execute(outputStream, 0)); + assertThrows(TimeoutException.class, () -> this.executor.execute(outputStream, 1, TimeUnit.NANOSECONDS)); assertEquals(0, outputStream.size()); - assertTrue(this.executor.isFinished()); } @Test - void testExecuteOnWrongCommand() { + void testExecuteOnFailed() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8 * 1024); - ProcessExecutor processExecutor = new ProcessExecutor("ttttt"); + ProcessExecutor processExecutor = new ProcessExecutor("javac", "-a"); assertThrows(IOException.class, () -> processExecutor.execute(outputStream)); - assertFalse(processExecutor.isFinished()); } + @Test + void testExecuteOnNotFoundCommand() { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8 * 1024); + ProcessExecutor processExecutor = new ProcessExecutor("ttttt"); + assertThrows(IOException.class, () -> processExecutor.execute(outputStream)); + } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java index d136b1a78..9ad660ff3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java @@ -25,7 +25,7 @@ class ProcessManagerTest extends AbstractTestCase { @Test - void test() throws Throwable { + void test() { ProcessManager processManager = INSTANCE; ProcessExecutor processExecutor = new ProcessExecutor("java", "-version"); ExecutorService executorService = newFixedThreadPool(1); @@ -34,7 +34,7 @@ void test() throws Throwable { long timeout = timeUnit.toMillis(2); Future future = executorService.submit(() -> { processExecutor.execute(outputStream, timeout); - return processExecutor.isFinished(); + return true; }); while (!future.isDone()) { diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java index 510a75501..9174d67d4 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java @@ -10,7 +10,6 @@ import javax.annotation.Nonnull; import java.io.File; -import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Array; import java.math.BigDecimal; @@ -514,7 +513,7 @@ void testResolvePackageNameOnPrimitiveType() { } @Test - void testFindClassNamesInClassPathWithFilePath() throws IOException { + void testFindClassNamesInClassPathWithFilePath() throws Exception { assertFindClassNamesMethod(ClassUtilsTest.class, ClassUtils::findClassNamesInClassPath); assertFindClassNamesMethod(Nonnull.class, ClassUtils::findClassNamesInClassPath); From 9aea4d118a272be9ae979860296f9591a634c8a2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 18:07:30 +0800 Subject: [PATCH 114/438] Make ProcessExecutor use instance ExecutorService Replace the previously static ExecutorService with a per-instance executor in ProcessExecutor to avoid a shared static thread pool and lifecycle/shutdown issues. Each ProcessExecutor now creates its own single-thread executor and is registered with shutdownOnExit. Also update junit-platform.properties to disable parallel test execution (junit.jupiter.execution.parallel.enabled = false) to reduce test flakiness. --- .../java/io/microsphere/process/ProcessExecutor.java | 12 +++++------- .../src/test/resources/junit-platform.properties | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java index 4e0d7af2e..eb16198cd 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java @@ -60,8 +60,6 @@ public class ProcessExecutor { private static final Logger logger = getLogger(ProcessExecutor.class); - private static final ExecutorService executor = newSingleThreadExecutor(newThreadFactory("process-exec", true)); - /** * The default proeprty value for the timeout of process execution : 30 seconds */ @@ -94,13 +92,11 @@ public class ProcessExecutor { private final Runtime runtime = getRuntime(); - private final String commandLine; - private final String options; - static { - shutdownOnExit(executor); - } + private final String commandLine; + + private final ExecutorService executor; /** * Constructor @@ -116,7 +112,9 @@ public ProcessExecutor(String command, String... options) { } } this.options = optionsBuilder.toString(); + this.executor = newSingleThreadExecutor(newThreadFactory("process-exec", true)); this.commandLine = command + this.options; + shutdownOnExit(this.executor); } /** diff --git a/microsphere-java-core/src/test/resources/junit-platform.properties b/microsphere-java-core/src/test/resources/junit-platform.properties index 993f2b029..5c558927b 100644 --- a/microsphere-java-core/src/test/resources/junit-platform.properties +++ b/microsphere-java-core/src/test/resources/junit-platform.properties @@ -1,3 +1,3 @@ junit.jupiter.extensions.autodetection.enabled = true -junit.jupiter.execution.parallel.enabled = true \ No newline at end of file +junit.jupiter.execution.parallel.enabled = false \ No newline at end of file From cf2a338071e8e27f732baa102548de3d4fd409de Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 18:16:21 +0800 Subject: [PATCH 115/438] Wait for process with timeout before checking exit Call process.waitFor(timeout, timeUnit) before reading process.exitValue() so the executor waits up to the configured timeout for the process to finish. This avoids attempting to read the exit value while the process may still be running. --- .../src/main/java/io/microsphere/process/ProcessExecutor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java index eb16198cd..5f297b8f8 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java @@ -163,6 +163,8 @@ public void execute(OutputStream outputStream, long timeout, TimeUnit timeUnit) copy(processInputStream, targetOutputStream); // Copy the error input stream copy(processErrorInputStream, targetOutputStream); + process.waitFor(timeout, timeUnit); + exitValue = process.exitValue(); if (exitValue != 0) { throw new IOException(); From 160361b30f4ddaa3b00befc64b5b69feabc285b6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 20:13:44 +0800 Subject: [PATCH 116/438] Wait for process and improve error message Ensure the executor waits for the spawned process and surface a clearer error when it exits with a non-zero status. Added a static import for format and call process.waitFor(timeout, timeUnit) before checking exitValue; construct and throw an IOException with a formatted message that includes the command and exit code. Updated tests to use a reusable FastByteArrayOutputStream (with tearDown closing it), refactored tests to create local ProcessExecutor instances (using `javac --help`), and adjusted expectations for timeout and failure scenarios to match the new behavior. --- .../microsphere/process/ProcessExecutor.java | 7 +++- .../process/ProcessExecutorTest.java | 38 ++++++++++--------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java index 5f297b8f8..78dddd87e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java @@ -21,6 +21,7 @@ import static io.microsphere.io.IOUtils.copy; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.process.ProcessManager.INSTANCE; +import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.ArrayUtils.isNotEmpty; import static io.microsphere.util.ExceptionUtils.wrap; import static java.lang.Long.getLong; @@ -163,11 +164,15 @@ public void execute(OutputStream outputStream, long timeout, TimeUnit timeUnit) copy(processInputStream, targetOutputStream); // Copy the error input stream copy(processErrorInputStream, targetOutputStream); + + // wait for the process being executed process.waitFor(timeout, timeUnit); + // try to exit with value exitValue = process.exitValue(); if (exitValue != 0) { - throw new IOException(); + String message = format("The command['{}'] execution is exited with invalid value : ", commandLine, exitValue); + throw new IOException(message); } } finally { processManager.removeUnfinishedProcess(process, options); diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java index bd7abe382..dca488eb0 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java @@ -1,12 +1,12 @@ package io.microsphere.process; import io.microsphere.AbstractTestCase; +import io.microsphere.io.FastByteArrayOutputStream; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -22,41 +22,43 @@ */ class ProcessExecutorTest extends AbstractTestCase { - private ProcessExecutor executor; + private FastByteArrayOutputStream outputStream; @BeforeEach void setUp() { - this.executor = new ProcessExecutor("java", "-version"); + this.outputStream = new FastByteArrayOutputStream(8 * 1024); + } + + @AfterEach + void tearDown() { + this.outputStream.close(); } @Test void testExecute() throws Exception { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8 * 1024); - this.executor.execute(outputStream); - assertTrue(outputStream.size() > 0); - String response = new String(outputStream.toByteArray()); + ProcessExecutor processExecutor = new ProcessExecutor("javac", "--help"); + processExecutor.execute(this.outputStream); + assertTrue(this.outputStream.size() > 0); + String response = this.outputStream.toString(); log(response); } @Test void testExecuteWithTimeout() { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8 * 1024); - assertThrows(TimeoutException.class, () -> this.executor.execute(outputStream, 0)); - assertThrows(TimeoutException.class, () -> this.executor.execute(outputStream, 1, TimeUnit.NANOSECONDS)); - assertEquals(0, outputStream.size()); + ProcessExecutor processExecutor = new ProcessExecutor("javac", "--help"); + assertThrows(TimeoutException.class, () -> processExecutor.execute(this.outputStream, 1)); + assertEquals(0, this.outputStream.size()); } @Test void testExecuteOnFailed() { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8 * 1024); ProcessExecutor processExecutor = new ProcessExecutor("javac", "-a"); - assertThrows(IOException.class, () -> processExecutor.execute(outputStream)); + assertThrows(IOException.class, () -> processExecutor.execute(this.outputStream)); } @Test void testExecuteOnNotFoundCommand() { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8 * 1024); - ProcessExecutor processExecutor = new ProcessExecutor("ttttt"); - assertThrows(IOException.class, () -> processExecutor.execute(outputStream)); + ProcessExecutor processExecutor = new ProcessExecutor("not-found-command"); + assertThrows(IOException.class, () -> processExecutor.execute(this.outputStream)); } -} +} \ No newline at end of file From 725a6523a9c0c833be2c9bf612c8143fe02e1fd2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 20:15:55 +0800 Subject: [PATCH 117/438] Add private constructor, remove destroy Make ProcessManager non-instantiable by adding a private no-arg constructor and remove the public destroy(Process) method. Keeps existing unfinished process tracking methods and the unfinishedProcessesMap() accessor; this tightens the API and prevents external instantiation. --- .../main/java/io/microsphere/process/ProcessManager.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessManager.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessManager.java index 1166e2609..67a6b4930 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessManager.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessManager.java @@ -46,6 +46,9 @@ public class ProcessManager { private final ConcurrentMap unfinishedProcessesCache = new ConcurrentHashMap<>(); + private ProcessManager() { + } + protected ProcessManager addUnfinishedProcess(Process process, String arguments) { unfinishedProcessesCache.putIfAbsent(process, arguments); return this; @@ -56,10 +59,6 @@ protected ProcessManager removeUnfinishedProcess(Process process, String argumen return this; } - public void destroy(Process process) { - process.destroy(); - } - /** * Unfinished Processes Map * @@ -70,4 +69,4 @@ public void destroy(Process process) { public Map unfinishedProcessesMap() { return unmodifiableMap(unfinishedProcessesCache); } -} +} \ No newline at end of file From 568cc6003e578558e7cad4d7e24d08b0eedd6942 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 9 Feb 2026 20:38:32 +0800 Subject: [PATCH 118/438] Simplify reflective resolution and update tests Remove redundant transient boolean flags used to track lazy resolution in ReflectiveDefinition, MemberDefinition, and ExecutableDefinition and rely on null checks for resolved fields. Replace some null/boolean checks with Objects.nonNull, and tighten equals implementations (add self-check and class-instance checks) for consistency. Update AbstractReflectiveDefinitionTest to use ArrayUtils.ofArray for constructing test inputs, expand test cases and assertions, and add a new skeleton ReflectiveDefinitionTest with empty test methods. --- .../reflect/ExecutableDefinition.java | 10 ++- .../microsphere/reflect/MemberDefinition.java | 13 ++-- .../reflect/ReflectiveDefinition.java | 19 +++--- .../AbstractReflectiveDefinitionTest.java | 17 ++++- .../reflect/ReflectiveDefinitionTest.java | 67 +++++++++++++++++++ 5 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableDefinition.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableDefinition.java index f11401214..ce1aeab4b 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableDefinition.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableDefinition.java @@ -66,8 +66,6 @@ public abstract class ExecutableDefinition extends MemberD @Nonnull protected final String[] parameterClassNames; - private transient boolean resolvedParameterTypes; - @Nonnull private transient Class[] parameterTypes; @@ -137,17 +135,17 @@ public final String[] getParameterClassNames() { */ @Nonnull public final Class[] getParameterTypes() { - if (!this.resolvedParameterTypes && this.parameterTypes == null) { + if (this.parameterTypes == null) { this.parameterTypes = resolveParameterTypes(this.parameterClassNames); - this.resolvedParameterTypes = true; } return this.parameterTypes.clone(); } @Override public boolean equals(Object o) { - if (!(o instanceof ExecutableDefinition)) return false; - if (!super.equals(o)) return false; + if (!super.equals(o)) { + return false; + } ExecutableDefinition that = (ExecutableDefinition) o; return arrayEquals(this.parameterClassNames, that.parameterClassNames); diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MemberDefinition.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MemberDefinition.java index 2d250a025..b837fe059 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MemberDefinition.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MemberDefinition.java @@ -25,6 +25,7 @@ import java.lang.reflect.Member; import static io.microsphere.util.Version.ofVersion; +import static java.util.Objects.nonNull; /** * The definition class for Java Reflection {@link Member}. @@ -81,8 +82,6 @@ public abstract class MemberDefinition extends ReflectiveDefin @Nullable private transient M member; - private boolean resolvedMember; - /** * @param since the 'since' version * @param declaredClassName the name of declared class @@ -166,22 +165,22 @@ public final Class getDeclaredClass() { */ @Nullable public final M getMember() { - if (!resolvedMember && member == null) { + if (member == null) { member = resolveMember(); - resolvedMember = true; } return member; } @Override public boolean isPresent() { - return getMember() != null; + return nonNull(getMember()); } @Override public boolean equals(Object o) { - if (!(o instanceof MemberDefinition)) return false; - if (!super.equals(o)) return false; + if (!super.equals(o)) { + return false; + } MemberDefinition that = (MemberDefinition) o; return this.name.equals(that.name); diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectiveDefinition.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectiveDefinition.java index abcf516f5..52fc3ba3b 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectiveDefinition.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectiveDefinition.java @@ -32,6 +32,7 @@ import static io.microsphere.util.ClassLoaderUtils.resolveClass; import static io.microsphere.util.Version.ofVersion; import static java.util.Objects.hash; +import static java.util.Objects.nonNull; /** * The abstract definition class for Java Reflection. @@ -101,8 +102,6 @@ public abstract class ReflectiveDefinition implements Serializable { @Nonnull protected final String className; - private transient boolean resolved; - @Nullable private transient Class resolvedClass; @@ -142,7 +141,6 @@ protected ReflectiveDefinition(@Nonnull Version since, @Nullable Deprecation dep this.since = since; this.deprecation = deprecation; this.className = className; - this.resolved = false; } /** @@ -182,10 +180,9 @@ public final String getClassName() { */ @Nullable public final Class getResolvedClass() { - if (!resolved && resolvedClass == null) { + if (resolvedClass == null) { ClassLoader classLoader = getClassLoader(getClass()); resolvedClass = resolveClass(className, classLoader, true); - resolved = true; } return resolvedClass; } @@ -194,7 +191,7 @@ public final Class getResolvedClass() { * Whether the member is deprecated */ public final boolean isDeprecated() { - return deprecation != null; + return nonNull(deprecation); } /** @@ -206,7 +203,13 @@ public final boolean isDeprecated() { @Override public boolean equals(Object o) { - if (!(o instanceof ReflectiveDefinition)) return false; + if (this == o) { + return true; + } + + if (!(this.getClass().isInstance(o))) { + return false; + } ReflectiveDefinition that = (ReflectiveDefinition) o; return this.since.equals(that.since) @@ -228,4 +231,4 @@ public String toString() { ", resolvedClass=" + this.getResolvedClass() + '}'; } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java index f16864350..fb87a8127 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java @@ -12,6 +12,7 @@ import static io.microsphere.lang.DeprecationTest.DEPRECATION; import static io.microsphere.lang.DeprecationTest.SINCE; import static io.microsphere.reflect.ConstructorUtils.findConstructor; +import static io.microsphere.util.ArrayUtils.ofArray; import static io.microsphere.util.Version.ofVersion; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -30,12 +31,13 @@ */ public abstract class AbstractReflectiveDefinitionTest { - private final List[] headConstructorArgumentsArray = new List[]{ + private final List[] headConstructorArgumentsArray = ofArray( ofList(SINCE, getClassName()), ofList(ofVersion(SINCE), getClassName()), ofList(SINCE, DEPRECATION, getClassName()), - ofList(ofVersion(SINCE), DEPRECATION, getClassName()) - }; + ofList(ofVersion(SINCE), DEPRECATION, getClassName()), + ofList(ofVersion("2.0.0"), DEPRECATION, getClassName()) + ); protected List definitions; @@ -85,6 +87,7 @@ void testGetDeprecation() { assertNull(definitions.get(1).getDeprecation()); assertNotNull(definitions.get(2).getDeprecation()); assertNotNull(definitions.get(3).getDeprecation()); + assertNotNull(definitions.get(4).getDeprecation()); } @Test @@ -98,6 +101,7 @@ void testGetClassName() { void testGetResolvedClass() { for (D definition : definitions) { assertNotNull(definition.getResolvedClass()); + assertNotNull(definition.getResolvedClass()); } } @@ -107,6 +111,7 @@ void testIsDeprecated() { assertFalse(definitions.get(1).isDeprecated()); assertTrue(definitions.get(2).isDeprecated()); assertTrue(definitions.get(3).isDeprecated()); + assertTrue(definitions.get(4).isDeprecated()); } @Test @@ -118,9 +123,13 @@ void testIsPresent() { @Test void testTestEquals() { + assertEquals(definitions.get(0), definitions.get(0)); assertEquals(definitions.get(0), definitions.get(1)); assertEquals(definitions.get(2), definitions.get(3)); assertNotEquals(definitions.get(0), definitions.get(2)); + assertNotEquals(definitions.get(0), definitions.get(2)); + assertNotEquals(definitions.get(0), definitions.get(4)); + assertFalse(definitions.get(0).equals(this)); } @Test @@ -128,6 +137,7 @@ void testTestHashCode() { assertEquals(definitions.get(0).hashCode(), definitions.get(1).hashCode()); assertEquals(definitions.get(2).hashCode(), definitions.get(3).hashCode()); assertNotEquals(definitions.get(0).hashCode(), definitions.get(2).hashCode()); + assertNotEquals(definitions.get(0).hashCode(), definitions.get(4).hashCode()); } @Test @@ -135,5 +145,6 @@ void testTestToString() { assertEquals(definitions.get(0).toString(), definitions.get(1).toString()); assertEquals(definitions.get(2).toString(), definitions.get(3).toString()); assertNotEquals(definitions.get(0).toString(), definitions.get(2).toString()); + assertNotEquals(definitions.get(0).toString(), definitions.get(4).toString()); } } \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java new file mode 100644 index 000000000..13cdee6ac --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java @@ -0,0 +1,67 @@ +/* + * 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. + */ + +package io.microsphere.reflect; + + +import org.junit.jupiter.api.Test; + +/** + * {@link ReflectiveDefinition} Test + * + * @author Mercy + * @see ReflectiveDefinition + * @since 1.0.0 + */ +class ReflectiveDefinitionTest { + + @Test + void testGetSince() { + } + + @Test + void testGetDeprecation() { + } + + @Test + void testGetClassName() { + } + + @Test + void testGetResolvedClass() { + } + + @Test + void testIsDeprecated() { + } + + @Test + void testIsPresent() { + } + + @Test + void testTestEquals() { + } + + @Test + void testTestHashCode() { + } + + @Test + void testTestToString() { + } +} \ No newline at end of file From 1a0897eec022235c81fa78a24134b85746b4d48b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 10 Feb 2026 10:44:53 +0800 Subject: [PATCH 119/438] Validate Deprecation.level and simplify equals Enforce non-null Level in Deprecation by adding an assertNotNull check and changing the constructor parameter to @Nonnull, removing the previous null-to-DEFAULT fallback. Simplify equals() with an identity check before instanceof. Update unit tests to use static imports, cover identity/equality/inequality cases, and reflect the DEFAULT Level behavior where appropriate. --- .../java/io/microsphere/lang/Deprecation.java | 16 ++++-- .../io/microsphere/lang/DeprecationTest.java | 50 +++++++++++-------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/lang/Deprecation.java b/microsphere-java-core/src/main/java/io/microsphere/lang/Deprecation.java index a19eb0a01..135c872e8 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/lang/Deprecation.java +++ b/microsphere-java-core/src/main/java/io/microsphere/lang/Deprecation.java @@ -26,6 +26,7 @@ import static io.microsphere.constants.SymbolConstants.QUOTE_CHAR; import static io.microsphere.lang.Deprecation.Level.DEFAULT; +import static io.microsphere.util.Assert.assertNotNull; import static java.util.Objects.hash; /** @@ -96,12 +97,13 @@ public final class Deprecation implements Serializable { } Deprecation(@Nullable Version since, @Nullable String replacement, @Nullable String reason, - @Nullable String link, @Nullable Level level) { + @Nullable String link, @Nonnull Level level) { + assertNotNull(level, () -> "the 'level' must not be null"); this.since = since; this.replacement = replacement; this.reason = reason; this.link = link; - this.level = level == null ? DEFAULT : level; + this.level = level; } @Nullable @@ -129,10 +131,14 @@ public Level getLevel() { return level; } - @Override public boolean equals(Object o) { - if (!(o instanceof Deprecation)) return false; + if (o == this) { + return true; + } + if (!(o instanceof Deprecation)) { + return false; + } Deprecation that = (Deprecation) o; return Objects.equals(since, that.since) && Objects.equals(replacement, that.replacement) @@ -270,4 +276,4 @@ public static Deprecation of(String since, String replacement, String reason, St .level(level) .build(); } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/DeprecationTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/DeprecationTest.java index 9aedf1083..41f398f8e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/DeprecationTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/DeprecationTest.java @@ -16,12 +16,19 @@ */ package io.microsphere.lang; +import io.microsphere.lang.Deprecation.Level; import io.microsphere.util.Version; import org.junit.jupiter.api.Test; +import static io.microsphere.lang.Deprecation.Level.DEFAULT; +import static io.microsphere.lang.Deprecation.Level.REMOVAL; +import static io.microsphere.lang.Deprecation.of; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link Deprecation} Test @@ -40,13 +47,16 @@ public class DeprecationTest { public static final String LINK = "https://github.com/microsphere-projects/microsphere-java"; - public static Deprecation.Level LEVEL = Deprecation.Level.REMOVAL; + public static Level LEVEL = REMOVAL; - public static Deprecation DEPRECATION = Deprecation.of(SINCE, REPLACEMENT, REASON, LINK, LEVEL); + public static Deprecation DEPRECATION = of(SINCE, REPLACEMENT, REASON, LINK, LEVEL); @Test void test() { - Deprecation deprecation = Deprecation.of(SINCE, REPLACEMENT, REASON, LINK, LEVEL); + Deprecation deprecation = of(SINCE, REPLACEMENT, REASON, LINK, LEVEL); + + assertTrue(deprecation.equals(deprecation)); + assertFalse(deprecation.equals(LEVEL)); assertObjectMethods(deprecation); assertEquals(Version.of(SINCE), deprecation.getSince()); @@ -57,61 +67,59 @@ void test() { assertNotNull(deprecation.toString()); assertEquals(deprecation, new Deprecation(deprecation)); assertEquals(deprecation.hashCode(), new Deprecation(deprecation).hashCode()); + assertNotEquals(of(SINCE, REPLACEMENT, REASON, LINK), deprecation); - deprecation = Deprecation.of(SINCE, REPLACEMENT, REASON, LINK); + deprecation = of(SINCE, REPLACEMENT, REASON, LINK); assertObjectMethods(deprecation); assertEquals(Version.of(SINCE), deprecation.getSince()); assertEquals(REPLACEMENT, deprecation.getReplacement()); assertEquals(REASON, deprecation.getReason()); assertEquals(LINK, deprecation.getLink()); - assertEquals(Deprecation.Level.DEFAULT, deprecation.getLevel()); + assertEquals(DEFAULT, deprecation.getLevel()); assertNotNull(deprecation.toString()); assertEquals(deprecation, new Deprecation(deprecation)); + assertEquals(deprecation, of(SINCE, REPLACEMENT, REASON, LINK, null)); assertEquals(deprecation.hashCode(), new Deprecation(deprecation).hashCode()); + assertNotEquals(of(SINCE, REPLACEMENT, REASON, LINK + ".git"), deprecation); - deprecation = Deprecation.of(SINCE, REPLACEMENT, REASON); - assertObjectMethods(deprecation); - assertEquals(Version.of(SINCE), deprecation.getSince()); - assertEquals(REPLACEMENT, deprecation.getReplacement()); - assertEquals(REASON, deprecation.getReason()); - assertNull(deprecation.getLink()); - assertEquals(Deprecation.Level.DEFAULT, deprecation.getLevel()); - assertNotNull(deprecation.toString()); - assertEquals(deprecation, new Deprecation(deprecation)); - assertEquals(deprecation.hashCode(), new Deprecation(deprecation).hashCode()); - deprecation = Deprecation.of(SINCE, REPLACEMENT, REASON); + deprecation = of(SINCE, REPLACEMENT, REASON); assertObjectMethods(deprecation); assertEquals(Version.of(SINCE), deprecation.getSince()); assertEquals(REPLACEMENT, deprecation.getReplacement()); assertEquals(REASON, deprecation.getReason()); assertNull(deprecation.getLink()); - assertEquals(Deprecation.Level.DEFAULT, deprecation.getLevel()); + assertEquals(DEFAULT, deprecation.getLevel()); assertNotNull(deprecation.toString()); assertEquals(deprecation, new Deprecation(deprecation)); assertEquals(deprecation.hashCode(), new Deprecation(deprecation).hashCode()); + assertNotEquals(of(SINCE, REPLACEMENT, "Reason"), deprecation); - deprecation = Deprecation.of(SINCE, REPLACEMENT); + + deprecation = of(SINCE, REPLACEMENT); assertObjectMethods(deprecation); assertEquals(Version.of(SINCE), deprecation.getSince()); assertEquals(REPLACEMENT, deprecation.getReplacement()); assertNull(deprecation.getReason()); assertNull(deprecation.getLink()); - assertEquals(Deprecation.Level.DEFAULT, deprecation.getLevel()); + assertEquals(DEFAULT, deprecation.getLevel()); assertNotNull(deprecation.toString()); assertEquals(deprecation, new Deprecation(deprecation)); assertEquals(deprecation.hashCode(), new Deprecation(deprecation).hashCode()); + assertNotEquals(of(SINCE, "Replacement"), deprecation); + - deprecation = Deprecation.of(SINCE); + deprecation = of(SINCE); assertObjectMethods(deprecation); assertEquals(Version.of(SINCE), deprecation.getSince()); assertNull(deprecation.getReplacement()); assertNull(deprecation.getReason()); assertNull(deprecation.getLink()); - assertEquals(Deprecation.Level.DEFAULT, deprecation.getLevel()); + assertEquals(DEFAULT, deprecation.getLevel()); assertNotNull(deprecation.toString()); assertEquals(deprecation, new Deprecation(deprecation)); assertEquals(deprecation.hashCode(), new Deprecation(deprecation).hashCode()); + assertNotEquals(of("1.2.3"), deprecation); } From b073b6f13cfa07936e487fdce7e697b454b757d3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 10 Feb 2026 12:22:47 +0800 Subject: [PATCH 120/438] Fix log format and use java -version in test Add the missing '{}' placeholder to the error message in ProcessExecutor so the exit value is included in the formatted string. Update the unit test to invoke `java -version` instead of `javac --help`, using a more reliable command that produces output in typical runtime environments. --- .../src/main/java/io/microsphere/process/ProcessExecutor.java | 2 +- .../test/java/io/microsphere/process/ProcessExecutorTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java index 78dddd87e..5c49e3f10 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java @@ -171,7 +171,7 @@ public void execute(OutputStream outputStream, long timeout, TimeUnit timeUnit) // try to exit with value exitValue = process.exitValue(); if (exitValue != 0) { - String message = format("The command['{}'] execution is exited with invalid value : ", commandLine, exitValue); + String message = format("The command['{}'] execution is exited with invalid value : {}", commandLine, exitValue); throw new IOException(message); } } finally { diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java index dca488eb0..557aa516d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java @@ -36,7 +36,7 @@ void tearDown() { @Test void testExecute() throws Exception { - ProcessExecutor processExecutor = new ProcessExecutor("javac", "--help"); + ProcessExecutor processExecutor = new ProcessExecutor("java", "-version"); processExecutor.execute(this.outputStream); assertTrue(this.outputStream.size() > 0); String response = this.outputStream.toString(); From 9fc5545009e0c6af62e7a4efcc118ab26fc9df31 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 16:27:45 +0800 Subject: [PATCH 121/438] Use static getLogger import in test Add static import for LoggerFactory.getLogger and update logger initialization in MethodDefinitionTest to use the static getLogger method. This is a refactor to simplify the logger reference with no behavioral change. --- .../test/java/io/microsphere/reflect/MethodDefinitionTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java index 7267473fb..7f00f6eb7 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java @@ -23,6 +23,7 @@ import java.util.List; import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.ArrayUtils.ofArray; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -36,7 +37,7 @@ */ class MethodDefinitionTest extends AbstractExecutableDefinitionTest { - private static final Logger logger = LoggerFactory.getLogger(MethodDefinitionTest.class); + private static final Logger logger = getLogger(MethodDefinitionTest.class); @Override protected List getTailConstructorArguments() { From 82a14a3ac80b96174ed2ce87f2238a9a77c12ed4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 16:27:53 +0800 Subject: [PATCH 122/438] Use static import for getLogger StopWatchTest was updated to add a static import for getLogger and replace the explicit LoggerFactory.getLogger(...) call with getLogger(...). This simplifies the code style in the test and introduces no functional change. --- .../src/test/java/io/microsphere/util/StopWatchTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java index ba110fd9c..941e4327d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test; import static io.microsphere.constants.SymbolConstants.SPACE; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.StopWatch.Task.start; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -42,7 +43,7 @@ */ class StopWatchTest { - private static final Logger logger = LoggerFactory.getLogger(StopWatchTest.class); + private static final Logger logger = getLogger(StopWatchTest.class); private static final String testName = "test"; From e4ff52f829c4d9f416a1094455d7a06ffb187b7e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 16:57:36 +0800 Subject: [PATCH 123/438] Refactor AbstractConverter logging and equals Use static getLogger import and simplify logger field initialization; change error handling in convert() to log as warn and wrap exceptions as IllegalArgumentException instead of RuntimeException; remove redundant target null initialization; add identity short-circuit in equals() for faster equality checks and clearer semantics. These changes improve logging, exception semantics, and minor code cleanup. --- .../convert/AbstractConverter.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/convert/AbstractConverter.java b/microsphere-java-core/src/main/java/io/microsphere/convert/AbstractConverter.java index 4ce41c1ca..e8176e284 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/convert/AbstractConverter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/convert/AbstractConverter.java @@ -21,11 +21,11 @@ import io.microsphere.annotation.Nullable; import io.microsphere.lang.Prioritized; import io.microsphere.logging.Logger; -import io.microsphere.logging.LoggerFactory; import java.util.List; import java.util.Objects; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.ClassUtils.getAllClasses; import static io.microsphere.util.ClassUtils.getTypeName; import static io.microsphere.util.ExceptionUtils.wrap; @@ -87,7 +87,7 @@ */ public abstract class AbstractConverter implements Converter { - protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected final Logger logger = getLogger(getClass()); @Nullable private Integer priority; @@ -102,14 +102,12 @@ public final T convert(@Nullable S source) { if (source == null) { return null; } - T target = null; + T target; try { target = doConvert(source); } catch (Throwable e) { - if (logger.isTraceEnabled()) { - logger.trace("The source[value : {}] can't be converted by the Converter[class : '{}']", source, getTypeName(getClass())); - } - throw wrap(e, RuntimeException.class); + logger.warn("The source[value : {}] can't be converted by the Converter[class : '{}']", source, getTypeName(getClass())); + throw wrap(e, IllegalArgumentException.class); } return target; } @@ -155,7 +153,12 @@ public int getPriority() { @Override public boolean equals(Object o) { - if (!(o instanceof AbstractConverter)) return false; + if (o == this) { + return true; + } + if (!(o instanceof AbstractConverter)) { + return false; + } AbstractConverter that = (AbstractConverter) o; return Objects.equals(getSourceType(), that.getSourceType()) From 140a87b60c43521af656e3cfb49de4d0ce49f8fe Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 16:57:44 +0800 Subject: [PATCH 124/438] Refactor AbstractConverterTest & add tests Change AbstractConverterTest to use String generic types instead of Object and update related method signatures. Add import for assertThrows and a new test (testConvertOnFailed) that verifies convert(...) wraps conversion errors as IllegalArgumentException. Simplify testConvertIfPossible (now empty) and extend equality checks to assert the converter is not equal to StringToBooleanConverter.INSTANCE or ObjectToStringConverter.INSTANCE. These changes improve type-safety and add coverage for failure and equality behavior. --- .../convert/AbstractConverterTest.java | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java index 5148e64ad..cae84a6d2 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java @@ -23,6 +23,7 @@ import static io.microsphere.lang.Prioritized.NORMAL_PRIORITY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * {@link AbstractConverter} Test @@ -31,14 +32,14 @@ * @see AbstractConverter * @since 1.0.0 */ -class AbstractConverterTest extends BaseConverterTest { +class AbstractConverterTest extends BaseConverterTest { @Override - protected AbstractConverter createConverter() { - return new AbstractConverter() { + protected AbstractConverter createConverter() { + return new AbstractConverter() { @Override - protected Object doConvert(Object source) throws Throwable { + protected String doConvert(String source) throws Throwable { return source; } @@ -56,12 +57,12 @@ public int getPriority() { } @Override - protected Object getSource() throws Throwable { + protected String getSource() throws Throwable { return "test"; } @Override - protected Object getTarget() throws Throwable { + protected String getTarget() throws Throwable { return "test"; } @@ -71,8 +72,19 @@ void testGetPriority() { } @Test - void testConvertIfPossible() throws Throwable { + void testConvertIfPossible() { + } + + @Test + void testConvertOnFailed() { + AbstractConverter converter = new AbstractConverter() { + @Override + protected String doConvert(String source) throws Throwable { + throw new Throwable("For testing"); + } + }; + assertThrows(IllegalArgumentException.class, () -> converter.convert(getSource())); } @Test @@ -85,5 +97,7 @@ void testGetConverter() { @Test void testEquals() { assertEquals(this.converter, this.converter); + assertNotEquals(this.converter, StringToBooleanConverter.INSTANCE); + assertNotEquals(this.converter, ObjectToStringConverter.INSTANCE); } } From 09e7bf423a983b93c9e96943f056bae6ff859033 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 17:02:27 +0800 Subject: [PATCH 125/438] Wrap conversion errors in RuntimeException Change AbstractConverter to wrap caught Throwables in RuntimeException instead of IllegalArgumentException to better represent arbitrary conversion failures. Update AbstractConverterTest to expect RuntimeException accordingly. --- .../src/main/java/io/microsphere/convert/AbstractConverter.java | 2 +- .../test/java/io/microsphere/convert/AbstractConverterTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/convert/AbstractConverter.java b/microsphere-java-core/src/main/java/io/microsphere/convert/AbstractConverter.java index e8176e284..9f465c8ab 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/convert/AbstractConverter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/convert/AbstractConverter.java @@ -107,7 +107,7 @@ public final T convert(@Nullable S source) { target = doConvert(source); } catch (Throwable e) { logger.warn("The source[value : {}] can't be converted by the Converter[class : '{}']", source, getTypeName(getClass())); - throw wrap(e, IllegalArgumentException.class); + throw wrap(e, RuntimeException.class); } return target; } diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java index cae84a6d2..26bf13fa1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java @@ -84,7 +84,7 @@ protected String doConvert(String source) throws Throwable { } }; - assertThrows(IllegalArgumentException.class, () -> converter.convert(getSource())); + assertThrows(RuntimeException.class, () -> converter.convert(getSource())); } @Test From 0ef92918c9b85ec48d0ae037faed62a34a4657c6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 18:51:16 +0800 Subject: [PATCH 126/438] Make Filter extend Predicate and add tests Extend Filter with java.util.function.Predicate and provide a default test(T) implementation that delegates to accept(T) for compatibility with the Java functional API. Add FilterTest to verify both accept(...) and test(...) behavior using a simple String-based predicate. --- .../java/io/microsphere/filter/Filter.java | 11 +++- .../io/microsphere/filter/FilterTest.java | 54 +++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/filter/FilterTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/filter/Filter.java b/microsphere-java-core/src/main/java/io/microsphere/filter/Filter.java index 912e24704..5850f5118 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/filter/Filter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/filter/Filter.java @@ -3,6 +3,8 @@ */ package io.microsphere.filter; +import java.util.function.Predicate; + /** * The {@code Filter} interface represents a generic filtering mechanism that can be applied to objects of type {@code T}. *

    @@ -29,7 +31,7 @@ * @since 1.0.0 */ @FunctionalInterface -public interface Filter { +public interface Filter extends Predicate { /** * Does accept filtered object? @@ -38,4 +40,9 @@ public interface Filter { * @return */ boolean accept(T filteredObject); -} + + @Override + default boolean test(T t) { + return accept(t); + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/filter/FilterTest.java b/microsphere-java-core/src/test/java/io/microsphere/filter/FilterTest.java new file mode 100644 index 000000000..eae613363 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/filter/FilterTest.java @@ -0,0 +1,54 @@ +/* + * 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. + */ + +package io.microsphere.filter; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link Filter} Test + * + * @author Mercy + * @see Filter + * @since 1.0.0 + */ +class FilterTest { + + private Filter filter; + + @BeforeEach + void setUp() { + this.filter = value -> "true".equalsIgnoreCase(value); + } + + @Test + void testAccept() { + assertFalse(this.filter.accept("test")); + assertTrue(this.filter.accept("true")); + } + + @Test + void testTest() { + assertFalse(this.filter.test("test")); + assertTrue(this.filter.test("true")); + } +} \ No newline at end of file From bd2e8b935546e02fee1d22c7dae7b4e667ee56a2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 21:44:34 +0800 Subject: [PATCH 127/438] Refactor test temp-file helpers and logging visibility Restrict test logging methods to protected, centralize temporary file/directory creation under TEST_TEMP_DIR, and ensure created files/directories are marked deleteOnExit. Replace UUID.randomUUID() calls with a static randomUUID import and remove the now-unneeded newTempFile helper. Adds convenience methods createRandomTempFile() and newRandomTempFile(), and consolidates random filename generation. --- .../java/io/microsphere/AbstractTestCase.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java index 9f8e167a8..c9be0e306 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java +++ b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.Queue; import java.util.Set; -import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Consumer; @@ -40,6 +39,7 @@ import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; +import static java.util.UUID.randomUUID; import static java.util.concurrent.ThreadLocalRandom.current; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -128,56 +128,50 @@ public abstract class AbstractTestCase { protected final Logger logger = getLogger(getClass()); - public void log(Object object) { + protected void log(Object object) { if (logger.isTraceEnabled()) { logger.trace(valueOf(object)); } } - public void log(String object, Object... args) { + protected void log(String object, Object... args) { if (logger.isTraceEnabled()) { logger.trace(object, args); } } protected File createRandomTempDirectory() { - File tempDir = newTempFile(buildRandomFileName()); - assertTrue(tempDir.mkdir()); - return tempDir; + return createRandomDirectory(TEST_TEMP_DIR); + } + + protected File createRandomTempFile() throws IOException { + return createRandomFile(TEST_TEMP_DIR); + } + + protected File newRandomTempFile() { + return newRandomFile(TEST_TEMP_DIR); } protected File createRandomDirectory(File parentDir) { File tempDir = newRandomFile(parentDir); assertTrue(tempDir.mkdir()); + tempDir.deleteOnExit(); return tempDir; } - protected File createRandomTempFile() throws IOException { - File randomTempFile = newRandomTempFile(); - assertTrue(randomTempFile.createNewFile()); - return randomTempFile; - } - protected File createRandomFile(File parentDir) throws IOException { File randomFile = newRandomFile(parentDir); assertTrue(randomFile.createNewFile()); + randomFile.deleteOnExit(); return randomFile; } - protected File newRandomTempFile() { - return newTempFile(buildRandomFileName()); - } - protected File newRandomFile(File parentDir) { return new File(parentDir, buildRandomFileName()); } protected String buildRandomFileName() { - return UUID.randomUUID().toString(); - } - - protected File newTempFile(String path) { - return new File(TEST_TEMP_DIR, path); + return randomUUID().toString(); } protected File makeLinkFile(File targetFile) throws Exception { From 843818389938d101ec2957d50c279f04c8975386 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 21:44:55 +0800 Subject: [PATCH 128/438] Refactor JarUtils: simplify toJarFile/doExtract Simplify and harden JarUtils internals: - Replace verbose toJarFile implementation with a concise ternary return. - Convert anonymous JarEntryFilter to a lambda for readability. - Change doExtract signature from Iterable to Collection, add null/empty guard (uses CollectionUtils.isEmpty) and early return to avoid NPEs. - Move resource closing into finally block and keep extraction logic unchanged. These changes improve readability and null-safety; note the doExtract signature change may require updating callers. --- .../io/microsphere/util/jar/JarUtils.java | 69 +++++++++---------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java index a36ab6787..e7a8756ba 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java @@ -16,12 +16,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URL; +import java.util.Collection; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import static io.microsphere.collection.CollectionUtils.isEmpty; import static io.microsphere.collection.ListUtils.ofList; import static io.microsphere.constants.ProtocolConstants.FILE_PROTOCOL; import static io.microsphere.constants.ProtocolConstants.JAR_PROTOCOL; @@ -73,12 +75,8 @@ public abstract class JarUtils implements Utils { */ @Nullable public static JarFile toJarFile(URL jarURL) throws IOException { - JarFile jarFile = null; final String jarAbsolutePath = resolveJarAbsolutePath(jarURL); - if (jarAbsolutePath == null) - return null; - jarFile = new JarFile(jarAbsolutePath); - return jarFile; + return jarAbsolutePath == null ? null : new JarFile(jarAbsolutePath); } /** @@ -341,14 +339,11 @@ public static void extract(URL jarResourceURL, File targetDirectory, JarEntryFil final String relativePath = resolveRelativePath(jarResourceURL); final JarEntry jarEntry = jarFile.getJarEntry(relativePath); final boolean isDirectory = jarEntry.isDirectory(); - List jarEntriesList = filter(jarFile, new JarEntryFilter() { - @Override - public boolean accept(JarEntry filteredObject) { - String name = filteredObject.getName(); - if (isDirectory && name.equals(relativePath)) { - return true; - } else return name.startsWith(relativePath); - } + List jarEntriesList = filter(jarFile, entry -> { + String name = entry.getName(); + if (isDirectory && name.equals(relativePath)) { + return true; + } else return name.startsWith(relativePath); }); jarEntriesList = doFilter(jarEntriesList, jarEntryFilter); @@ -356,30 +351,31 @@ public boolean accept(JarEntry filteredObject) { doExtract(jarFile, jarEntriesList, targetDirectory); } - protected static void doExtract(JarFile jarFile, Iterable jarEntries, File targetDirectory) throws IOException { - if (jarEntries != null) { - for (JarEntry jarEntry : jarEntries) { - String jarEntryName = jarEntry.getName(); - File targetFile = new File(targetDirectory, jarEntryName); - if (jarEntry.isDirectory()) { - targetFile.mkdirs(); - } else { - InputStream inputStream = null; - OutputStream outputStream = null; - try { - inputStream = jarFile.getInputStream(jarEntry); - if (inputStream != null) { - File parentFile = targetFile.getParentFile(); - if (!parentFile.exists()) { - parentFile.mkdirs(); - } - outputStream = new FileOutputStream(targetFile); - copy(inputStream, outputStream); + protected static void doExtract(JarFile jarFile, Collection jarEntries, File targetDirectory) throws IOException { + if (jarFile == null || isEmpty(jarEntries)) { + return; + } + for (JarEntry jarEntry : jarEntries) { + String jarEntryName = jarEntry.getName(); + File targetFile = new File(targetDirectory, jarEntryName); + if (jarEntry.isDirectory()) { + targetFile.mkdirs(); + } else { + InputStream inputStream = null; + OutputStream outputStream = null; + try { + inputStream = jarFile.getInputStream(jarEntry); + if (inputStream != null) { + File parentFile = targetFile.getParentFile(); + if (!parentFile.exists()) { + parentFile.mkdirs(); } - } finally { - close(outputStream); - close(inputStream); + outputStream = new FileOutputStream(targetFile); + copy(inputStream, outputStream); } + } finally { + close(outputStream); + close(inputStream); } } } @@ -387,5 +383,4 @@ protected static void doExtract(JarFile jarFile, Iterable jarEntries, private JarUtils() { } - -} +} \ No newline at end of file From 6da6cdcafdeaa7e64d4ebd329ccd3ba7bf79db2a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 21:45:08 +0800 Subject: [PATCH 129/438] Refactor JarUtilsTest and add coverage Convert JarUtilsTest to extend AbstractTestCase and replace manual temp-dir handling with createRandomTempDirectory. Clean up imports and use ClassLoaderUtils helpers consistently. Add/adjust tests to improve coverage and robustness: filter behavior, toJarFile edge cases (null, invalid protocol, not-found), assertJarURLProtocol, resolveRelative/absolute paths, findJarEntry, extract variations (URL overload, doExtract with nulls/missing entries) and lambdas for JarEntryFilter. Overall this refactors setup and expands test cases to catch more error conditions and behaviors. --- .../io/microsphere/util/jar/JarUtilsTest.java | 107 ++++++++++++------ 1 file changed, 74 insertions(+), 33 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java index c398bfc82..7cb27a7d7 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java @@ -3,8 +3,8 @@ */ package io.microsphere.util.jar; +import io.microsphere.AbstractTestCase; import io.microsphere.filter.JarEntryFilter; -import io.microsphere.util.ClassLoaderUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -12,23 +12,29 @@ import java.io.File; import java.io.IOException; import java.net.URL; +import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import static io.microsphere.io.FileUtils.deleteDirectory; import static io.microsphere.net.URLUtils.ofURL; +import static io.microsphere.util.ClassLoaderUtils.ResourceType.PACKAGE; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; import static io.microsphere.util.ClassLoaderUtils.getClassResource; -import static io.microsphere.util.SystemUtils.JAVA_IO_TMPDIR; +import static io.microsphere.util.ClassLoaderUtils.getResource; import static io.microsphere.util.jar.JarUtils.MANIFEST_RESOURCE_PATH; import static io.microsphere.util.jar.JarUtils.assertJarURLProtocol; +import static io.microsphere.util.jar.JarUtils.doExtract; import static io.microsphere.util.jar.JarUtils.extract; +import static io.microsphere.util.jar.JarUtils.filter; import static io.microsphere.util.jar.JarUtils.findJarEntry; import static io.microsphere.util.jar.JarUtils.resolveJarAbsolutePath; import static io.microsphere.util.jar.JarUtils.resolveRelativePath; import static io.microsphere.util.jar.JarUtils.toJarFile; +import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -39,21 +45,18 @@ * @see JarUtilsTest * @since 1.0.0 */ -class JarUtilsTest { +class JarUtilsTest extends AbstractTestCase { - private final static File tempDirectory = new File(JAVA_IO_TMPDIR); - - private final static File targetDirectory = new File(tempDirectory, "jar-util-extract"); + private File targetDirectory; private final ClassLoader classLoader = getClassLoader(getClass()); private URL resourceURL; @BeforeEach - void setUp() throws IOException { - deleteDirectory(targetDirectory); - targetDirectory.mkdirs(); - this.resourceURL = getClassResource(classLoader, Nonnull.class); + void setUp() { + this.targetDirectory = createRandomTempDirectory(); + this.resourceURL = getClassResource(this.classLoader, Nonnull.class); } @Test @@ -63,57 +66,95 @@ void testConstants() { @Test void testAssertJarURLProtocol() { - URL url = ofURL("http://localhost"); - assertThrows(IllegalArgumentException.class, () -> assertJarURLProtocol(url)); + assertThrows(IllegalArgumentException.class, () -> assertJarURLProtocol(ofURL("http://localhost"))); + assertJarURLProtocol(ofURL("file://localhost")); } @Test void testResolveRelativePath() { - String relativePath = resolveRelativePath(resourceURL); + String relativePath = resolveRelativePath(this.resourceURL); String expectedPath = "javax/annotation/Nonnull.class"; assertEquals(expectedPath, relativePath); } @Test - void testResolveJarAbsolutePath() throws Exception { - String jarAbsolutePath = resolveJarAbsolutePath(resourceURL); + void testResolveJarAbsolutePath() { + String jarAbsolutePath = resolveJarAbsolutePath(this.resourceURL); assertNotNull(jarAbsolutePath); } @Test - void testToJarFile() throws Exception { - JarFile jarFile = toJarFile(resourceURL); + void testFilter() throws IOException { + JarFile jarFile = toJarFile(this.resourceURL); + List jarEntries = filter(jarFile, null); + assertFalse(jarEntries.isEmpty()); + } + + @Test + void testFilterOnNullJarFile() { + assertSame(emptyList(), filter(null, null)); + } + + @Test + void testToJarFile() throws IOException { + JarFile jarFile = toJarFile(this.resourceURL); assertNotNull(jarFile); } - public void testToJarFileOnException() throws Exception { - assertThrows(IllegalArgumentException.class, () -> { - URL url = new URL("http://www.google.com"); - JarFile jarFile = toJarFile(url); - }); + @Test + void testToJarFileOnNotFound() throws IOException { + URL url = new URL("jar:file:/path/to/file.jar!/entry"); + JarFile jarFile = toJarFile(url); + assertNull(jarFile); + } + + @Test + void testToJarFileOnInvalidProtocol() { + assertThrows(IllegalArgumentException.class, () -> toJarFile(new URL("http://github.com"))); + } + + @Test + void testToJarFileOnNPE() { + assertThrows(NullPointerException.class, () -> toJarFile(null)); } @Test void testFindJarEntry() throws Exception { - URL resourceURL = getClassResource(classLoader, Nonnull.class); + URL resourceURL = getClassResource(this.classLoader, Nonnull.class); JarEntry jarEntry = findJarEntry(resourceURL); assertNotNull(jarEntry); } @Test void testExtract() throws IOException { - String jarAbsolutePath = resolveJarAbsolutePath(resourceURL); - extract(new File(jarAbsolutePath), targetDirectory); + String jarAbsolutePath = resolveJarAbsolutePath(this.resourceURL); + extract(new File(jarAbsolutePath), this.targetDirectory); } @Test void testExtractWithURL() throws IOException { - URL resourceURL = ClassLoaderUtils.getResource(classLoader, ClassLoaderUtils.ResourceType.PACKAGE, "javax.annotation"); - extract(resourceURL, targetDirectory, new JarEntryFilter() { - @Override - public boolean accept(JarEntry filteredObject) { - return !filteredObject.isDirectory(); - } - }); + extract(this.resourceURL, this.targetDirectory, (JarEntryFilter) filteredObject -> !filteredObject.isDirectory()); + + URL resourceURL = getResource(this.classLoader, PACKAGE, "javax.annotation"); + extract(resourceURL, this.targetDirectory, (JarEntryFilter) filteredObject -> !filteredObject.isDirectory()); + } + + @Test + void testDoExtractOnNullJarFile() throws IOException { + doExtract(null, null, this.targetDirectory); + } + + @Test + void testDoExtractWithoutJarEntry() throws IOException { + JarFile jarFile = toJarFile(this.resourceURL); + doExtract(jarFile, null, this.targetDirectory); + } + + @Test + void testDoExtractOnMissingMatch() throws IOException { + URL resourceURL = getClassResource(this.classLoader, Test.class); + JarFile jarFile = toJarFile(resourceURL); + List jarEntries = filter(jarFile, null); + doExtract(toJarFile(this.resourceURL), jarEntries, this.targetDirectory); } } From 551712d36d1cbc440b0a4e26f99bfbaa7e879b22 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 21:54:26 +0800 Subject: [PATCH 130/438] Simplify loadHandlers and remove emptyMap check Remove unused Collections.emptyMap import and the early-return check for an empty handlers list. The loadHandlers method was made package-private (removed @Nonnull/@Immutable and private modifier) and now directly builds the handlers map with toFixedMap, simplifying the logic and removing redundant code. --- .../net/ServiceLoaderURLStreamHandlerFactory.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/ServiceLoaderURLStreamHandlerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/net/ServiceLoaderURLStreamHandlerFactory.java index ece8723fe..debae146f 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/ServiceLoaderURLStreamHandlerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/ServiceLoaderURLStreamHandlerFactory.java @@ -30,7 +30,6 @@ import static io.microsphere.collection.MapUtils.toFixedMap; import static io.microsphere.net.URLUtils.attachURLStreamHandlerFactory; import static io.microsphere.util.ServiceLoaderUtils.loadServicesList; -import static java.util.Collections.emptyMap; /** * A {@link URLStreamHandlerFactory} implementation that uses the JDK's {@link ServiceLoader} @@ -84,18 +83,10 @@ private static URLStreamHandlerFactory createDelegate() { @Nonnull @Immutable - private static Map loadHandlers() { + static Map loadHandlers() { List handlers = loadServicesList(ExtendableProtocolURLStreamHandler.class); - - int size = handlers.size(); - if (size < 1) { - return emptyMap(); - } - Map handlersMap = toFixedMap( handlers, handler -> immutableEntry(handler.getProtocol(), handler)); - return handlersMap; - } -} +} \ No newline at end of file From efd0f90768fdc41ba90907e23beaf9c915e52188 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 21:57:34 +0800 Subject: [PATCH 131/438] Use Objects.hash and instanceof in equals Simplify equals and hashCode implementations in TypeArgument: switch the class equality check to an instanceof check, add clearer brace formatting, and replace the manual hash computation with java.util.Objects.hash (via static import). These changes improve readability and delegate hash calculation to the standard library. --- .../reflect/generics/TypeArgument.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/generics/TypeArgument.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/generics/TypeArgument.java index b35669c00..1b1075fb6 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/generics/TypeArgument.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/generics/TypeArgument.java @@ -21,6 +21,7 @@ import static io.microsphere.util.Assert.assertNotNull; import static io.microsphere.util.Assert.assertTrue; +import static java.util.Objects.hash; /** * {@link Type} Argument @@ -59,20 +60,26 @@ public int getIndex() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + + if (!(o instanceof TypeArgument)) { + return false; + } TypeArgument that = (TypeArgument) o; - if (index != that.index) return false; + if (index != that.index) { + return false; + } + return Objects.equals(type, that.type); } @Override public int hashCode() { - int result = type != null ? type.hashCode() : 0; - result = 31 * result + index; - return result; + return hash(this.type, this.index); } From e87eda4ae92e339999ce905d755b15f0ecc7419c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 22:08:03 +0800 Subject: [PATCH 132/438] Add NameFileFilter unit tests Introduce NameFileFilterTest covering NameFileFilter behavior for exact matches and case variations. Tests assert accept() returns true for an exact filename and verifies both case-sensitive and case-insensitive matching scenarios. Also includes a minor EOF newline normalization in NameFileFilter.java. --- .../microsphere/io/filter/NameFileFilter.java | 2 +- .../io/filter/NameFileFilterTest.java | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/io/filter/NameFileFilterTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/filter/NameFileFilter.java b/microsphere-java-core/src/main/java/io/microsphere/io/filter/NameFileFilter.java index 1e53e2231..c71c2032d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/filter/NameFileFilter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/filter/NameFileFilter.java @@ -46,4 +46,4 @@ public boolean accept(File file) { String name = this.name; return caseSensitive ? fileName.equals(name) : fileName.equalsIgnoreCase(name); } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/filter/NameFileFilterTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/filter/NameFileFilterTest.java new file mode 100644 index 000000000..b30f7c637 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/filter/NameFileFilterTest.java @@ -0,0 +1,70 @@ +/* + * 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. + */ + +package io.microsphere.io.filter; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link NameFileFilter} Test + * + * @author Mercy + * @see NameFileFilter + * @since 1.0.0 + */ +class NameFileFilterTest { + + private static final String FILE_NAME = "Test.txt"; + + private File file; + + @BeforeEach + void setUp() { + this.file = new File(FILE_NAME); + } + + @Test + void testAccept() { + NameFileFilter filter = new NameFileFilter(FILE_NAME); + assertTrue(filter.accept(this.file)); + + filter = new NameFileFilter(FILE_NAME.toUpperCase()); + assertFalse(filter.accept(this.file)); + + filter = new NameFileFilter(FILE_NAME.toLowerCase()); + assertFalse(filter.accept(this.file)); + } + + @Test + void testAcceptOnIgnoreCaseSensitive() { + NameFileFilter filter = new NameFileFilter(FILE_NAME, false); + assertTrue(filter.accept(this.file)); + + filter = new NameFileFilter(FILE_NAME.toUpperCase(), false); + assertTrue(filter.accept(this.file)); + + filter = new NameFileFilter(FILE_NAME.toLowerCase(), false); + assertTrue(filter.accept(this.file)); + } +} \ No newline at end of file From 99e64d6826e7db1083f7d610c144b05eb9b5fbc3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 11 Feb 2026 22:11:20 +0800 Subject: [PATCH 133/438] Use nonNull and make isPresent non-final Replace direct null comparison with Objects.nonNull via a static import for readability, and remove the final modifier from isPresent to allow overriding in subclasses. Minor whitespace/formatting adjustments. --- .../java/io/microsphere/reflect/ClassDefinition.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ClassDefinition.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ClassDefinition.java index 2446c5e93..4a2bef849 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ClassDefinition.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ClassDefinition.java @@ -22,6 +22,8 @@ import io.microsphere.lang.Deprecation; import io.microsphere.util.Version; +import static java.util.Objects.nonNull; + /** * A concrete implementation of {@link ReflectiveDefinition} representing the definition of a Java class. * @@ -89,8 +91,7 @@ public ClassDefinition(@Nonnull Version since, @Nullable Deprecation deprecation } @Override - public final boolean isPresent() { - return super.getResolvedClass() != null; + public boolean isPresent() { + return nonNull(super.getResolvedClass()); } - -} +} \ No newline at end of file From 804dfbd8126ac656f1ac482201b93270c62e1afe Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 12 Feb 2026 13:43:30 +0800 Subject: [PATCH 134/438] Remove redundant logger checks and simplify IO flow Clean up StreamArtifactResourceResolver by removing unnecessary logger.isTraceEnabled()/isErrorEnabled() guards and calling logger methods directly. Simplify readArtifactMetadataDataFromArchiveFile by returning early and preferring the directory branch, removing an intermediate variable. Minor tidy-up of input stream handling and trace messages; no API changes intended. --- .../StreamArtifactResourceResolver.java | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/StreamArtifactResourceResolver.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/StreamArtifactResourceResolver.java index 052e0bda5..58ed3de1a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/StreamArtifactResourceResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/StreamArtifactResourceResolver.java @@ -113,9 +113,7 @@ public final Artifact resolve(URL resourceURL) { Artifact artifact = null; try { if (archiveFile == null) { - if (logger.isTraceEnabled()) { - logger.trace("The resourceURL['{}'] can't be resolved to be an archive file", resourceURL); - } + logger.trace("The resourceURL['{}'] can't be resolved to be an archive file", resourceURL); artifactMetadataData = readArtifactMetadataDataFromResource(resourceURL, classLoader); } else { artifactMetadataData = readArtifactMetadataDataFromArchiveFile(archiveFile); @@ -124,9 +122,7 @@ public final Artifact resolve(URL resourceURL) { artifact = resolve(resourceURL, artifactMetadataData, classLoader); } } catch (IOException e) { - if (logger.isErrorEnabled()) { - logger.error("The Artifact can't be resolved from the resource URL : {}", resourceURL, e); - } + logger.error("The Artifact can't be resolved from the resource URL : {}", resourceURL, e); } finally { // close the InputStream close(artifactMetadataData); @@ -141,13 +137,11 @@ protected InputStream readArtifactMetadataDataFromResource(URL resourceURL, Clas @Nullable protected InputStream readArtifactMetadataDataFromArchiveFile(File archiveFile) throws IOException { - InputStream artifactMetadataData = null; - if (archiveFile.isFile()) { - artifactMetadataData = readArtifactMetadataDataFromFile(archiveFile); - } else if (archiveFile.isDirectory()) { - artifactMetadataData = readArtifactMetadataDataFromDirectory(archiveFile); + if (archiveFile.isDirectory()) { + return readArtifactMetadataDataFromDirectory(archiveFile); + } else { + return readArtifactMetadataDataFromFile(archiveFile); } - return artifactMetadataData; } @Nullable @@ -155,9 +149,7 @@ protected InputStream readArtifactMetadataDataFromFile(File archiveFile) throws JarFile jarFile = new JarFile(archiveFile); JarEntry jarEntry = findArtifactMetadataEntry(jarFile); if (jarEntry == null) { - if (logger.isTraceEnabled()) { - logger.trace("The artifact metadata entry can't be resolved from the JarFile[path: '{}']", archiveFile); - } + logger.trace("The artifact metadata entry can't be resolved from the JarFile[path: '{}']", archiveFile); return null; } return jarFile.getInputStream(jarEntry); @@ -167,9 +159,7 @@ protected InputStream readArtifactMetadataDataFromFile(File archiveFile) throws protected InputStream readArtifactMetadataDataFromDirectory(File directory) throws IOException { File artifactMetadataFile = findArtifactMetadata(directory); if (artifactMetadataFile == null) { - if (logger.isTraceEnabled()) { - logger.trace("The artifact metadata file can't be found in the directory[path: '{}']", directory); - } + logger.trace("The artifact metadata file can't be found in the directory[path: '{}']", directory); return null; } return new FileInputStream(artifactMetadataFile); @@ -197,4 +187,4 @@ protected boolean isArtifactMetadataFile(File directory, File file) { protected abstract boolean isArtifactMetadata(String relativePath); protected abstract Artifact resolve(URL resourceURL, InputStream artifactMetadataData, ClassLoader classLoader) throws IOException; -} +} \ No newline at end of file From a34e03b3f6a9d5d5b893addd97596bb8cbdaf544 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 12 Feb 2026 17:14:39 +0800 Subject: [PATCH 135/438] Remove getCodeSourceLocation; cleanup imports Remove the unused getCodeSourceLocation(Class) method from ClassDataRepository and eliminate several now-unused imports (File, URL, MalformedURLException, CodeSource, ProtectionDomain, StringUtils). Move the private constructor to the end of the class. Update tests to use the ClassDataRepository.INSTANCE constant, remove the test for getCodeSourceLocation, add negative tests for not-found classpath/package, and tidy up test imports and assertions accordingly. --- .../microsphere/lang/ClassDataRepository.java | 43 ++----------------- 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/lang/ClassDataRepository.java b/microsphere-java-core/src/main/java/io/microsphere/lang/ClassDataRepository.java index 05961ed23..812b135ff 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/lang/ClassDataRepository.java +++ b/microsphere-java-core/src/main/java/io/microsphere/lang/ClassDataRepository.java @@ -21,11 +21,6 @@ import io.microsphere.annotation.Nullable; import io.microsphere.util.ClassPathUtils; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.CodeSource; -import java.security.ProtectionDomain; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; @@ -38,7 +33,6 @@ import static io.microsphere.util.ClassPathUtils.getClassPaths; import static io.microsphere.util.ClassUtils.findClassNamesInClassPath; import static io.microsphere.util.ClassUtils.resolvePackageName; -import static io.microsphere.util.StringUtils.isNotBlank; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; @@ -94,9 +88,6 @@ public class ClassDataRepository { private final Map> packageNameToClassNamesMap = initPackageNameToClassNamesMap(); - private ClassDataRepository() { - } - /** * Get all package names in {@link ClassPathUtils#getClassPaths() class paths} * @@ -199,35 +190,6 @@ public Set getAllClassNamesInClassPaths() { return unmodifiableSet(allClassNames); } - /** - * Get {@link Class}'s code source location URL - * - * @param type - * @return If , return null. - * @throws NullPointerException If type is null , {@link NullPointerException} will be thrown. - */ - public URL getCodeSourceLocation(Class type) throws NullPointerException { - - URL codeSourceLocation = null; - ClassLoader classLoader = type.getClassLoader(); - - if (classLoader == null) { // Bootstrap ClassLoader or type is primitive or void - String path = findClassPath(type); - if (isNotBlank(path)) { - try { - codeSourceLocation = new File(path).toURI().toURL(); - } catch (MalformedURLException ignored) { - codeSourceLocation = null; - } - } - } else { - ProtectionDomain protectionDomain = type.getProtectionDomain(); - CodeSource codeSource = protectionDomain == null ? null : protectionDomain.getCodeSource(); - codeSourceLocation = codeSource == null ? null : codeSource.getLocation(); - } - return codeSourceLocation; - } - @Nonnull @Immutable private Map> initClassPathToClassNamesMap() { @@ -280,4 +242,7 @@ private Map> initPackageNameToClassNamesMap() { return unmodifiableMap(packageNameToClassNamesMap); } -} + + private ClassDataRepository() { + } +} \ No newline at end of file From 97f3ce2991b7aebe6414ae955288896262133d93 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 12 Feb 2026 17:14:44 +0800 Subject: [PATCH 136/438] Update ClassDataRepositoryTest.java --- .../lang/ClassDataRepositoryTest.java | 59 ++++++++----------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/ClassDataRepositoryTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/ClassDataRepositoryTest.java index 752b0eb86..be40025a9 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/ClassDataRepositoryTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/ClassDataRepositoryTest.java @@ -18,20 +18,18 @@ import io.microsphere.AbstractTestCase; import io.microsphere.reflect.ReflectionUtils; -import io.microsphere.util.VersionUtils; import org.junit.jupiter.api.Test; import javax.annotation.Nonnull; -import java.net.URL; import java.util.Map; import java.util.Set; +import static io.microsphere.lang.ClassDataRepository.INSTANCE; import static io.microsphere.util.ClassPathUtils.getClassPaths; -import static io.microsphere.util.VersionUtils.CURRENT_JAVA_VERSION; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ClassDataRepository} Test @@ -42,22 +40,28 @@ */ class ClassDataRepositoryTest extends AbstractTestCase { - private static final ClassDataRepository repository = ClassDataRepository.INSTANCE; + private final ClassDataRepository repository = INSTANCE; @Test void testGetClassNamesInClassPath() { Set classPaths = getClassPaths(); for (String classPath : classPaths) { - Set classNames = repository.getClassNamesInClassPath(classPath, true); + Set classNames = this.repository.getClassNamesInClassPath(classPath, true); assertNotNull(classNames); } } + @Test + void testGetClassNamesInClassPathOnNotFoundClassPath() { + Set classNames = this.repository.getClassNamesInClassPath("not-found", true); + assertTrue(classNames.isEmpty()); + } + @Test void testGetClassNamesInPackageWithName() { - Set packageNames = repository.getAllPackageNamesInClassPaths(); + Set packageNames = this.repository.getAllPackageNamesInClassPaths(); for (String packageName : packageNames) { - Set classNames = repository.getClassNamesInPackage(packageName); + Set classNames = this.repository.getClassNamesInPackage(packageName); assertFalse(classNames.isEmpty()); assertThrows(UnsupportedOperationException.class, classNames::clear); } @@ -66,31 +70,36 @@ void testGetClassNamesInPackageWithName() { @Test void testGetClassNamesInPackage() { Package pkg = ClassDataRepositoryTest.class.getPackage(); - Set classNames = repository.getClassNamesInPackage(pkg); + Set classNames = this.repository.getClassNamesInPackage(pkg); assertFalse(classNames.isEmpty()); assertThrows(UnsupportedOperationException.class, classNames::clear); } + @Test + void testGetClassNamesInPackageOnNotFoundPackage() { + Set classNames = this.repository.getClassNamesInPackage("not-found-package"); + assertTrue(classNames.isEmpty()); + } @Test void testGetAllPackageNamesInClassPaths() { - Set packageNames = repository.getAllPackageNamesInClassPaths(); + Set packageNames = this.repository.getAllPackageNamesInClassPaths(); assertFalse(packageNames.isEmpty()); assertThrows(UnsupportedOperationException.class, packageNames::clear); } @Test void testFindClassPath() { - String classPath = repository.findClassPath(ReflectionUtils.class); + String classPath = this.repository.findClassPath(ReflectionUtils.class); assertNotNull(classPath); - classPath = repository.findClassPath(Nonnull.class); + classPath = this.repository.findClassPath(Nonnull.class); assertNotNull(classPath); } @Test void testGetAllClassNamesMapInClassPath() { - Map> allClassNamesMapInClassPath = repository.getClassPathToClassNamesMap(); + Map> allClassNamesMapInClassPath = this.repository.getClassPathToClassNamesMap(); assertFalse(allClassNamesMapInClassPath.isEmpty()); assertThrows(UnsupportedOperationException.class, allClassNamesMapInClassPath::clear); @@ -98,28 +107,8 @@ void testGetAllClassNamesMapInClassPath() { @Test void testGetAllClassNamesInClassPath() { - Set allClassNames = repository.getAllClassNamesInClassPaths(); + Set allClassNames = this.repository.getAllClassNamesInClassPaths(); assertFalse(allClassNames.isEmpty()); assertThrows(UnsupportedOperationException.class, allClassNames::clear); - - } - - @Test - void testGetCodeSourceLocation() { - URL codeSourceLocation = null; - - codeSourceLocation = repository.getCodeSourceLocation(ClassDataRepositoryTest.class); - assertNotNull(codeSourceLocation); - - codeSourceLocation = repository.getCodeSourceLocation(Nonnull.class); - assertNotNull(codeSourceLocation); - - codeSourceLocation = repository.getCodeSourceLocation(String.class); - if (CURRENT_JAVA_VERSION.le(VersionUtils.JAVA_VERSION_8)) { - assertNotNull(codeSourceLocation); - } else { - assertNull(codeSourceLocation); - } } - -} +} \ No newline at end of file From 38f21643b2c364c180524e48c2f8640beda0177e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 12 Feb 2026 17:14:59 +0800 Subject: [PATCH 137/438] Remove unused LoggerFactory import Remove the unused io.microsphere.logging.LoggerFactory import from MethodDefinitionTest.java to clean up the test file and avoid unused-import warnings. --- .../test/java/io/microsphere/reflect/MethodDefinitionTest.java | 1 - .../src/test/java/io/microsphere/util/StopWatchTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java index 7f00f6eb7..18eaf093a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java @@ -17,7 +17,6 @@ package io.microsphere.reflect; import io.microsphere.logging.Logger; -import io.microsphere.logging.LoggerFactory; import org.junit.jupiter.api.Test; import java.util.List; diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java index 941e4327d..2e0ec9f22 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java @@ -17,7 +17,6 @@ package io.microsphere.util; import io.microsphere.logging.Logger; -import io.microsphere.logging.LoggerFactory; import io.microsphere.util.StopWatch.Task; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From 3aadf86b8013a01547fbbf1d81472b4a8ccd33bc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 12 Feb 2026 17:23:26 +0800 Subject: [PATCH 138/438] Use ClassUtils.getCodeSourceLocation in Version Replace direct dependency on ClassDataRepository in Version.java by using ClassUtils.getCodeSourceLocation. Removed ClassDataRepository import and static INSTANCE usage, added static import for getCodeSourceLocation, and updated the classResource lookup before throwing the error when Implementation-Version is missing. This simplifies the utility usage and reduces coupling. --- .../src/main/java/io/microsphere/util/Version.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/Version.java b/microsphere-java-core/src/main/java/io/microsphere/util/Version.java index 648702314..a1833ee54 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/Version.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/Version.java @@ -19,7 +19,6 @@ import io.microsphere.annotation.Immutable; import io.microsphere.annotation.Nonnull; import io.microsphere.annotation.Nullable; -import io.microsphere.lang.ClassDataRepository; import java.io.Serializable; import java.net.URL; @@ -36,11 +35,11 @@ import static io.microsphere.constants.SymbolConstants.LESS_THAN_OR_EQUAL_TO; import static io.microsphere.constants.SymbolConstants.QUOTE_CHAR; import static io.microsphere.constants.SymbolConstants.SPACE_CHAR; -import static io.microsphere.lang.ClassDataRepository.INSTANCE; import static io.microsphere.text.FormatUtils.format; import static io.microsphere.util.Assert.assertNotBlank; import static io.microsphere.util.Assert.assertNotNull; import static io.microsphere.util.Assert.assertTrue; +import static io.microsphere.util.ClassUtils.getCodeSourceLocation; import static io.microsphere.util.StringUtils.split; import static io.microsphere.util.Version.Operator.EQ; import static io.microsphere.util.Version.Operator.GE; @@ -404,8 +403,7 @@ public static Version getVersion(Class targetClass) throws IllegalArgumentExc Package targetPackage = targetClass.getPackage(); String version = targetPackage.getImplementationVersion(); if (version == null) { - ClassDataRepository repository = INSTANCE; - URL classResource = repository.getCodeSourceLocation(targetClass); + URL classResource = getCodeSourceLocation(targetClass); String errorMessage = format("The 'Implementation-Version' manifest attribute can't be fetched from the jar file[class resource : '{}'] by the target class[name :'{}']", classResource, targetClass.getName()); throw new IllegalArgumentException(errorMessage); } From 17725e2b1af537da2c23cf3efba3531c7c57005a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 12 Feb 2026 20:56:40 +0800 Subject: [PATCH 139/438] Improve FileWatchService lifecycle and logging Add proper lifecycle control and logging to StandardFileWatchService: introduce a Logger, AtomicBoolean 'started', isStarted/stop/close methods, and make event loop Future volatile. Reorder and initialize executors consistently, log directory registrations and dispatched events, and use MILLISECONDS wait in stop. Also convert several helper methods to static (toWatchEventKinds, toWatchEventKind, toKind) and tighten thread-safety when accessing shared state. --- .../io/StandardFileWatchService.java | 105 ++++++++++-------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/StandardFileWatchService.java b/microsphere-java-core/src/main/java/io/microsphere/io/StandardFileWatchService.java index 052a8a5f1..ba892d397 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/StandardFileWatchService.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/StandardFileWatchService.java @@ -22,6 +22,7 @@ import io.microsphere.io.event.FileChangedEvent; import io.microsphere.io.event.FileChangedEvent.Kind; import io.microsphere.io.event.FileChangedListener; +import io.microsphere.logging.Logger; import java.io.File; import java.nio.file.FileSystem; @@ -36,7 +37,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import static io.microsphere.annotation.ConfigurationProperty.SYSTEM_PROPERTIES_SOURCE; import static io.microsphere.collection.MapUtils.newTreeMap; @@ -49,6 +50,9 @@ import static io.microsphere.io.event.FileChangedEvent.Kind.CREATED; import static io.microsphere.io.event.FileChangedEvent.Kind.DELETED; import static io.microsphere.io.event.FileChangedEvent.Kind.MODIFIED; +import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.text.FormatUtils.format; +import static io.microsphere.util.ArrayUtils.arrayToString; import static io.microsphere.util.ArrayUtils.length; import static java.lang.System.getProperty; import static java.nio.file.FileSystems.getDefault; @@ -57,8 +61,8 @@ import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; -import static java.nio.file.StandardWatchEventKinds.OVERFLOW; import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; /** * /** @@ -103,6 +107,8 @@ */ public class StandardFileWatchService implements FileWatchService, AutoCloseable { + private static final Logger logger = getLogger(StandardFileWatchService.class); + /** * The default thread name prefix : "microsphere-file-watch-service" */ @@ -124,7 +130,7 @@ public class StandardFileWatchService implements FileWatchService, AutoCloseable ) public static final String THREAD_NAME_PREFIX = getProperty(THREAD_NAME_PREFIX_PROPERTY_NAME, DEFAULT_THREAD_NAME_PREFIX); - private static final WatchEvent.Kind[] ALL_WATCH_EVENT_KINDS = { + static final WatchEvent.Kind[] ALL_WATCH_EVENT_KINDS = { ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY @@ -132,15 +138,15 @@ public class StandardFileWatchService implements FileWatchService, AutoCloseable private WatchService watchService; - private final ExecutorService eventLoopExecutor; - private final Executor eventHandlerExecutor; + private final ExecutorService eventLoopExecutor; + private final Map fileChangedMetadataCache = newTreeMap(); - private volatile boolean started; + private final AtomicBoolean started; - private Future eventLoopFuture; + private volatile Future eventLoopFuture; public StandardFileWatchService() { this(DIRECT_EXECUTOR); @@ -151,18 +157,19 @@ public StandardFileWatchService(Executor eventHandlerExecutor) { } public StandardFileWatchService(Executor eventHandlerExecutor, ExecutorService eventLoopExecutor) { - this.eventLoopExecutor = eventLoopExecutor; this.eventHandlerExecutor = eventHandlerExecutor; + this.eventLoopExecutor = eventLoopExecutor; + this.started = new AtomicBoolean(false); // shutdown the ExecutorService when JVM exits shutdownOnExit(eventLoopExecutor, eventHandlerExecutor); } public void start() throws Exception { - if (started) { + if (!this.started.compareAndSet(false, true)) { throw new IllegalStateException("StandardFileWatchService has started"); } - started = true; + logger.info("Start to watch the file system."); FileSystem fileSystem = getDefault(); @@ -176,9 +183,32 @@ public void start() throws Exception { } + public void stop() throws Exception { + // set the flag "started" to false + while (this.started.compareAndSet(true, false)) { + // wait for the event loop to complete + this.eventLoopExecutor.awaitTermination(100, MILLISECONDS); + // if the event loop is not done, try to cancel the task + this.eventLoopFuture.cancel(true); + } + } + + public boolean isStarted() { + return this.started.get(); + } + + @Override + public void close() throws Exception { + this.stop(); + IOUtils.close(this.watchService); + this.fileChangedMetadataCache.clear(); + shutdown(this.eventLoopExecutor); + shutdown(this.eventHandlerExecutor); + } + private void dispatchFileChangedEvents(WatchService watchService) { - eventLoopFuture = eventLoopExecutor.submit(() -> { - while (started) { + this.eventLoopFuture = this.eventLoopExecutor.submit(() -> { + while (isStarted()) { WatchKey watchKey = null; try { watchKey = watchService.take(); @@ -188,7 +218,8 @@ private void dispatchFileChangedEvents(WatchService watchService) { Path dirPath = (Path) watchable; Path fileRelativePath = (Path) event.context(); - FileChangedMetadata metadata = fileChangedMetadataCache.get(dirPath); + FileChangedMetadata metadata = this.fileChangedMetadataCache.get(dirPath); + if (metadata != null) { Path filePath = dirPath.resolve(fileRelativePath); if (isDirectory(dirPath, NOFOLLOW_LINKS) || metadata.filePaths.contains(filePath)) { @@ -214,14 +245,17 @@ private void dispatchFileChangedEvent(Path filePath, WatchEvent.Kind watchEventK Kind kind = toKind(watchEventKind); FileChangedEvent fileChangedEvent = new FileChangedEvent(file, kind); eventDispatcher.dispatch(fileChangedEvent); + logger.trace("The {} was dispatched", fileChangedEvent); } private void registerDirectoriesToWatchService(WatchService watchService) throws Exception { - for (Map.Entry entry : fileChangedMetadataCache.entrySet()) { + for (Map.Entry entry : this.fileChangedMetadataCache.entrySet()) { Path directoryPath = entry.getKey(); FileChangedMetadata metadata = entry.getValue(); WatchEvent.Kind[] kinds = metadata.watchEventKinds; directoryPath.register(watchService, kinds); + logger.trace("The directory[path : '{}' , event kinds : {}] registers the WatchService : {}", + directoryPath, arrayToString(kinds), watchService); } } @@ -235,7 +269,7 @@ public void watch(File file, FileChangedListener listener, Kind... kinds) { private FileChangedMetadata getMetadata(Path filePath, Kind... kinds) { final Path dirPath = isDirectory(filePath, NOFOLLOW_LINKS) ? filePath : filePath.getParent(); - return fileChangedMetadataCache.computeIfAbsent(dirPath, k -> { + return this.fileChangedMetadataCache.computeIfAbsent(dirPath, k -> { FileChangedMetadata metadata = new FileChangedMetadata(); metadata.eventDispatcher = parallel(this.eventHandlerExecutor); metadata.watchEventKinds = toWatchEventKinds(kinds); @@ -244,7 +278,7 @@ private FileChangedMetadata getMetadata(Path filePath, Kind... kinds) { } @Nonnull - private WatchEvent.Kind[] toWatchEventKinds(Kind[] kinds) { + static WatchEvent.Kind[] toWatchEventKinds(Kind... kinds) { int size = length(kinds); if (size < 1) { return ALL_WATCH_EVENT_KINDS; @@ -258,8 +292,8 @@ private WatchEvent.Kind[] toWatchEventKinds(Kind[] kinds) { } @Nonnull - private WatchEvent.Kind toWatchEventKind(Kind kind) { - WatchEvent.Kind watchEventKind = OVERFLOW; + static WatchEvent.Kind toWatchEventKind(Kind kind) { + final WatchEvent.Kind watchEventKind; switch (kind) { case CREATED: watchEventKind = ENTRY_CREATE; @@ -267,7 +301,7 @@ private WatchEvent.Kind toWatchEventKind(Kind kind) { case MODIFIED: watchEventKind = ENTRY_MODIFY; break; - case DELETED: + default: watchEventKind = ENTRY_DELETE; break; } @@ -275,42 +309,21 @@ private WatchEvent.Kind toWatchEventKind(Kind kind) { } @Nonnull - private Kind toKind(WatchEvent.Kind watchEventKind) { + static Kind toKind(WatchEvent.Kind watchEventKind) { final Kind kind; if (ENTRY_CREATE.equals(watchEventKind)) { kind = CREATED; } else if (ENTRY_MODIFY.equals(watchEventKind)) { kind = MODIFIED; - } else { + } else if (ENTRY_DELETE.equals(watchEventKind)) { kind = DELETED; + } else { + String errorMessage = format("The invalid kind of WatchEvent : {}", watchEventKind); + throw new IllegalArgumentException(errorMessage); } return kind; } - public void stop() throws Exception { - if (started) { - // set the flag "started" to false - started = false; - // wait for the event loop to complete - if (!eventLoopExecutor.awaitTermination(100, TimeUnit.MILLISECONDS)) { - // if the event loop is not done, try to cancel the task - eventLoopFuture.cancel(true); - } - - if (watchService != null) { - watchService.close(); - } - fileChangedMetadataCache.clear(); - shutdown(eventLoopExecutor); - shutdown(eventHandlerExecutor); - } - } - - @Override - public void close() throws Exception { - this.stop(); - } - private static class FileChangedMetadata { private final Set filePaths = new TreeSet<>(); @@ -320,4 +333,4 @@ private static class FileChangedMetadata { private WatchEvent.Kind[] watchEventKinds; } -} +} \ No newline at end of file From f7a3da4def25e74f791d1ad5b056febfb61cb3c5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 12 Feb 2026 20:57:01 +0800 Subject: [PATCH 140/438] Add tests for StandardFileWatchService utils Add unit tests for StandardFileWatchService covering watch-event-kind conversions and service lifecycle. New tests validate toWatchEventKinds behavior (including null/empty inputs), mapping from StandardWatchEventKinds to FileChangedEvent.Kind via toKind, and that OVERFLOW is rejected. Existing file-watching tests updated to assert isStarted after start and isStopped after stop, and to explicitly stop the service. Added necessary static imports and assertions. --- .../io/StandardFileWatchServiceTest.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java index 7f5ca3601..6d6c0763d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java @@ -19,6 +19,7 @@ import io.microsphere.AbstractTestCase; import io.microsphere.io.event.DefaultFileChangedListener; import io.microsphere.io.event.FileChangedEvent; +import io.microsphere.io.event.FileChangedEvent.Kind; import io.microsphere.io.event.FileChangedListener; import io.microsphere.io.event.LoggingFileChangedListener; import io.microsphere.lang.function.ThrowableAction; @@ -37,19 +38,32 @@ import static io.microsphere.concurrent.ExecutorUtils.shutdown; import static io.microsphere.io.FileUtils.deleteDirectory; import static io.microsphere.io.FileUtils.forceDelete; +import static io.microsphere.io.StandardFileWatchService.ALL_WATCH_EVENT_KINDS; +import static io.microsphere.io.StandardFileWatchService.toKind; +import static io.microsphere.io.StandardFileWatchService.toWatchEventKinds; import static io.microsphere.io.event.FileChangedEvent.Kind.CREATED; import static io.microsphere.io.event.FileChangedEvent.Kind.DELETED; +import static io.microsphere.io.event.FileChangedEvent.Kind.MODIFIED; import static io.microsphere.io.event.FileChangedEvent.Kind.values; +import static io.microsphere.util.ArrayUtils.ofArray; import static io.microsphere.util.ClassLoaderUtils.getResource; import static io.microsphere.util.ExceptionUtils.wrap; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.Files.copy; import static java.nio.file.Files.write; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; +import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; +import static java.nio.file.StandardWatchEventKinds.OVERFLOW; import static java.util.concurrent.Executors.newSingleThreadExecutor; import static java.util.concurrent.ForkJoinPool.commonPool; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StandardFileWatchService} Test For File @@ -75,6 +89,34 @@ void tearDown() throws Exception { deleteDirectory(this.testDir); } + @Test + void testToWatchEventKinds() { + assertArrayEquals(ALL_WATCH_EVENT_KINDS, toWatchEventKinds()); + assertArrayEquals(ALL_WATCH_EVENT_KINDS, toWatchEventKinds(new Kind[0])); + assertArrayEquals(ALL_WATCH_EVENT_KINDS, toWatchEventKinds((Kind[]) null)); + assertArrayEquals(ALL_WATCH_EVENT_KINDS, toWatchEventKinds(CREATED, DELETED, MODIFIED)); + assertArrayEquals(ofArray(ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE), toWatchEventKinds(CREATED, MODIFIED, DELETED)); + } + + @Test + void testToWatchEventKind() { + assertArrayEquals(ALL_WATCH_EVENT_KINDS, toWatchEventKinds()); + assertArrayEquals(ALL_WATCH_EVENT_KINDS, toWatchEventKinds(new Kind[0])); + assertArrayEquals(ALL_WATCH_EVENT_KINDS, toWatchEventKinds((Kind[]) null)); + } + + @Test + void testToKind() { + assertEquals(CREATED, toKind(ENTRY_CREATE)); + assertEquals(MODIFIED, toKind(ENTRY_MODIFY)); + assertEquals(DELETED, toKind(ENTRY_DELETE)); + } + + @Test + void testToKindOnOverflow() { + assertThrows(IllegalArgumentException.class, () -> toKind(OVERFLOW)); + } + @Test void testFile() throws Exception { URL resource = getResource(super.classLoader, "test.txt"); @@ -93,9 +135,14 @@ void testFile() throws Exception { // start StandardFileWatchService fileWatchService.start(); + + assertTrue(fileWatchService.isStarted()); + // start again assertThrows(IllegalStateException.class, fileWatchService::start); + assertTrue(fileWatchService.isStarted()); + // copy file Path sourcePath = sourceFile.toPath(); Path targetFilePath = targetFile.toPath(); @@ -103,6 +150,10 @@ void testFile() throws Exception { // await for completion countDownLatch.await(); + + fileWatchService.stop(); + + assertFalse(fileWatchService.isStarted()); } } @@ -131,8 +182,12 @@ public void onFileDeleted(FileChangedEvent event) { fileWatchService.start(); + assertTrue(fileWatchService.isStarted()); + assertThrows(IllegalStateException.class, fileWatchService::start); + assertTrue(fileWatchService.isStarted()); + // create a test file File testFile = createRandomFile(testDir); @@ -146,6 +201,10 @@ public void onFileDeleted(FileChangedEvent event) { while (fileReference.get() == null) { // spin } + + fileWatchService.stop(); + + assertFalse(fileWatchService.isStarted()); } } From bd6be85ffecacf03a6f824d49ced773b1bf11c84 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 10:49:00 +0800 Subject: [PATCH 141/438] Simplify Windows path handling in URLUtils Refactor resolvePathFromFile to delegate to a new overload that accepts an isOsWindows flag (resolvePathFromFile(URL, boolean)), improving testability. Replace the previous manual index/substring logic with substringAfter(path, SLASH) to strip the leading slash on Windows. Add the required substringAfter import and minor cleanup. --- .../src/main/java/io/microsphere/net/URLUtils.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index e17f7fc67..04390ae03 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -60,6 +60,7 @@ import static io.microsphere.util.StringUtils.isBlank; import static io.microsphere.util.StringUtils.replace; import static io.microsphere.util.StringUtils.split; +import static io.microsphere.util.StringUtils.substringAfter; import static io.microsphere.util.StringUtils.substringAfterLast; import static io.microsphere.util.SystemUtils.FILE_ENCODING; import static io.microsphere.util.SystemUtils.IS_OS_WINDOWS; @@ -264,12 +265,13 @@ static String resolvePath(URL url, boolean includeArchiveEntryPath) { } static String resolvePathFromFile(URL url) { + return resolvePathFromFile(url, IS_OS_WINDOWS); + } + + static String resolvePathFromFile(URL url, boolean isOsWindows) { String path = buildPath(url); - if (IS_OS_WINDOWS) { - int index = path.indexOf(SLASH_CHAR); - if (index == 0) { - path = path.substring(1); - } + if (isOsWindows) { + path = substringAfter(path, SLASH); } return path; } From 919600de440fa00df9562185675d2dabe83c3154 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 10:49:13 +0800 Subject: [PATCH 142/438] Add resolvePathFromFile and buildPath tests Refactor test constants for query/matrix strings and add coverage for combined matrix+query case. Introduces tests for resolvePathFromFile (file:// path handling with/without leading slash) and buildPath to validate path extraction for file URLs. Also adds required static imports and minor test adjustments. --- .../java/io/microsphere/net/URLUtilsTest.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java index c5a442f91..5947751cb 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java @@ -33,6 +33,7 @@ import static io.microsphere.net.URLUtils.FILE_URL_PREFIX; import static io.microsphere.net.URLUtils.attachURLStreamHandlerFactory; import static io.microsphere.net.URLUtils.buildMatrixString; +import static io.microsphere.net.URLUtils.buildPath; import static io.microsphere.net.URLUtils.buildURI; import static io.microsphere.net.URLUtils.clearURLStreamHandlerFactory; import static io.microsphere.net.URLUtils.close; @@ -53,6 +54,7 @@ import static io.microsphere.net.URLUtils.resolveMatrixParameters; import static io.microsphere.net.URLUtils.resolveParameters; import static io.microsphere.net.URLUtils.resolvePath; +import static io.microsphere.net.URLUtils.resolvePathFromFile; import static io.microsphere.net.URLUtils.resolveProtocol; import static io.microsphere.net.URLUtils.resolveQueryParameters; import static io.microsphere.net.URLUtils.resolveSubProtocols; @@ -96,9 +98,15 @@ class URLUtilsTest extends AbstractTestCase { private static final String TEST_HTTP_WITH_PATH_HASH = TEST_HTTP_WITH_PATH + "#hash"; - private static final String TEST_HTTP_WITH_QUERY_STRING = TEST_HTTP_BASE + "?q=java&oq=java&sourceid=chrome&es_sm=122&ie=UTF-8"; + private static final String TEST_QUERY_STRING = "?q=java&oq=java&sourceid=chrome&es_sm=122&ie=UTF-8"; - private static final String TEST_HTTP_WITH_MATRIX_STRING = TEST_HTTP_BASE + ";q=java;oq=java;sourceid=chrome;es_sm=122;ie=UTF-8"; + private static final String TEST_MATRIX_STRING = ";q=java;oq=java;sourceid=chrome;es_sm=122;ie=UTF-8"; + + private static final String TEST_HTTP_WITH_QUERY_STRING = TEST_HTTP_BASE + TEST_QUERY_STRING; + + private static final String TEST_HTTP_WITH_MATRIX_STRING = TEST_HTTP_BASE + TEST_MATRIX_STRING; + + private static final String TEST_HTTP_WITH_MATRIX_QUERY_STRING = TEST_HTTP_BASE + TEST_MATRIX_STRING + TEST_QUERY_STRING; private static final String TEST_HTTP_WITH_SP_MATRIX = TEST_HTTP_BASE + ";_sp=text;_sp=properties"; @@ -207,6 +215,10 @@ void testResolveMatrixParameters() { assertEquals(expectedParametersMap, parametersMap); + url = TEST_HTTP_WITH_MATRIX_QUERY_STRING; + parametersMap = resolveMatrixParameters(url); + assertEquals(expectedParametersMap, parametersMap); + url = TEST_HTTP_WITH_QUERY_STRING; parametersMap = resolveMatrixParameters(url); expectedParametersMap = emptyMap(); @@ -559,6 +571,34 @@ void testResolveParametersOnEmptyString() { assertSame(emptyMap(), resolveParameters(EMPTY_STRING, AND_CHAR)); } + @Test + void testResolvePathFromFile() { + URL url = ofURL("file:///D:/test"); + assertEquals("D:/test", resolvePathFromFile(url, true)); + + ofURL("file://D:/test"); + assertEquals("D:/test", resolvePathFromFile(url, true)); + + ofURL("file:/D:/test"); + assertEquals("D:/test", resolvePathFromFile(url, true)); + + ofURL("file:test"); + assertEquals("D:/test", resolvePathFromFile(url, true)); + + ofURL("file://D:/test"); + assertEquals("/D:/test", resolvePathFromFile(url, false)); + } + + @Test + void testBuildPath() { + URL url = ofURL("file:///D:/test"); + String path = buildPath(url); + assertEquals(url.getPath(), path); + + url = ofURL("file:"); + path = buildPath(url); + assertEquals(url.getPath(), path); + } private void assertResolvePath(URL url) { assertResolvePath(url, true); From 59cf9abc3525731abfb24c00c68c26700975ea1b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 12:50:38 +0800 Subject: [PATCH 143/438] Add isDirectoryEntry and null-check in findJarEntry Add a new JarUtils.isDirectoryEntry(URL) method to detect whether a given 'jar:' URL refers to a directory entry (treating an empty relative path as the JAR root). The method only supports the 'jar' protocol, resolves the relative path, and uses findJarEntry to verify directory entries. Also add a null-check in findJarEntry to handle cases where toJarFile(url) returns null, and import StringUtils.EMPTY for the empty-path comparison. --- .../io/microsphere/util/jar/JarUtils.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java index e7a8756ba..463373930 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java @@ -34,6 +34,7 @@ import static io.microsphere.net.URLUtils.normalizePath; import static io.microsphere.net.URLUtils.resolveArchiveFile; import static io.microsphere.text.FormatUtils.format; +import static io.microsphere.util.StringUtils.EMPTY; import static io.microsphere.util.StringUtils.substringAfter; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; @@ -214,6 +215,9 @@ protected static List doFilter(Iterable jarEntries, JarEntry @Nullable public static JarEntry findJarEntry(URL jarURL) throws IOException { JarFile jarFile = toJarFile(jarURL); + if (jarFile == null) { + return null; + } final String relativePath = resolveRelativePath(jarURL); JarEntry jarEntry = jarFile.getJarEntry(relativePath); return jarEntry; @@ -351,6 +355,50 @@ public static void extract(URL jarResourceURL, File targetDirectory, JarEntryFil doExtract(jarFile, jarEntriesList, targetDirectory); } + + /** + * Determines whether the specified URL points to a directory entry within a JAR file. + * + *

    + * This method checks if the given URL represents a directory entry in a JAR archive. + * It supports only the "jar" protocol. For URLs with the "jar" protocol, it resolves + * the JAR file and verifies if the relative path corresponds to a directory entry. + * If the relative path is empty, it is treated as the root directory of the JAR. + *

    + * + *

    Example Usage

    + *
    {@code
    +     * URL jarURL = new URL("jar:file:/path/to/file.jar!/");
    +     * boolean isDirectory = JarUtils.isDirectoryEntry(jarURL);
    +     * System.out.println(isDirectory);  // Output: true
    +     *
    +     * URL fileURL = new URL("jar:file:/path/to/file.jar!/com/example/");
    +     * isDirectory = JarUtils.isDirectoryEntry(fileURL);
    +     * System.out.println(isDirectory);  // Output: true
    +     *
    +     * URL resourceURL = new URL("jar:file:/path/to/file.jar!/com/example/resource.txt");
    +     * isDirectory = JarUtils.isDirectoryEntry(resourceURL);
    +     * System.out.println(isDirectory);  // Output: false
    +     * }
    + * + * @param url the URL to check; must not be {@code null} + * @return {@code true} if the URL points to a directory entry in a JAR file, {@code false} otherwise + * @throws IOException if an I/O error occurs while resolving the JAR file or entry + */ + public static boolean isDirectoryEntry(URL url) throws IOException { + String protocol = url.getProtocol(); + if (JAR_PROTOCOL.equals(protocol)) { + final String relativePath = resolveRelativePath(url); + if (EMPTY.equals(relativePath)) { // root directory in jar + return true; + } else { + JarEntry jarEntry = findJarEntry(url); + return jarEntry != null && jarEntry.isDirectory(); + } + } + return false; + } + protected static void doExtract(JarFile jarFile, Collection jarEntries, File targetDirectory) throws IOException { if (jarFile == null || isEmpty(jarEntries)) { return; From c2a830dc40fbc4ace5b2f3fa82d7a4a47014426e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 12:50:45 +0800 Subject: [PATCH 144/438] Add isDirectoryEntry tests in JarUtilsTest Add a new test method testIsDirectoryEntry to cover JarUtils.isDirectoryEntry behavior across various URL types: a temp file URL (expected false), a package directory resource (expected true), a trimmed package URL (expected true), a class resource URL (expected false), and an explicit jar URL with trailing slash (expected false). Also add required static imports (substringBeforeLast, isDirectoryEntry, assertTrue). This improves coverage for directory-detection edge cases. --- .../io/microsphere/util/jar/JarUtilsTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java index 7cb27a7d7..33e11b93b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java @@ -21,12 +21,14 @@ import static io.microsphere.util.ClassLoaderUtils.getClassLoader; import static io.microsphere.util.ClassLoaderUtils.getClassResource; import static io.microsphere.util.ClassLoaderUtils.getResource; +import static io.microsphere.util.StringUtils.substringBeforeLast; import static io.microsphere.util.jar.JarUtils.MANIFEST_RESOURCE_PATH; import static io.microsphere.util.jar.JarUtils.assertJarURLProtocol; import static io.microsphere.util.jar.JarUtils.doExtract; import static io.microsphere.util.jar.JarUtils.extract; import static io.microsphere.util.jar.JarUtils.filter; import static io.microsphere.util.jar.JarUtils.findJarEntry; +import static io.microsphere.util.jar.JarUtils.isDirectoryEntry; import static io.microsphere.util.jar.JarUtils.resolveJarAbsolutePath; import static io.microsphere.util.jar.JarUtils.resolveRelativePath; import static io.microsphere.util.jar.JarUtils.toJarFile; @@ -37,6 +39,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link JarUtils} Test @@ -157,4 +160,24 @@ void testDoExtractOnMissingMatch() throws IOException { List jarEntries = filter(jarFile, null); doExtract(toJarFile(this.resourceURL), jarEntries, this.targetDirectory); } + + @Test + void testIsDirectoryEntry() throws IOException { + File tempFile = createRandomTempFile(); + resourceURL = tempFile.toURI().toURL(); + assertFalse(isDirectoryEntry(resourceURL)); + + URL resourceURL = getResource(this.classLoader, PACKAGE, "javax.annotation"); + assertTrue(isDirectoryEntry(resourceURL)); + + String path = substringBeforeLast(resourceURL.toString(), "javax/annotation"); + resourceURL = ofURL(path); + assertTrue(isDirectoryEntry(resourceURL)); + + resourceURL = getClassResource(this.classLoader, Nonnull.class); + assertFalse(isDirectoryEntry(resourceURL)); + + resourceURL = ofURL("jar:file:/path/to/file.jar!/com/acme/"); + assertFalse(isDirectoryEntry(resourceURL)); + } } From da39e384d96e875bf2bde15162222f065b14a866 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 13:39:28 +0800 Subject: [PATCH 145/438] Handle IO errors in JarUtils and add logging Add a Logger and URLUtils import to JarUtils and make JAR resolution more robust: toJarFile now returns null on IO errors (catching IOException and logging at trace) instead of propagating IOExceptions, and its signature was changed to throw IllegalArgumentException for invalid protocols. Removed declared IOExceptions from findJarEntry and isDirectoryEntry. Minor import and static logger initialization added to support these changes. --- .../io/microsphere/util/jar/JarUtils.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java index 463373930..0fd1a9d73 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java @@ -8,6 +8,8 @@ import io.microsphere.annotation.Nullable; import io.microsphere.constants.ProtocolConstants; import io.microsphere.filter.JarEntryFilter; +import io.microsphere.logging.Logger; +import io.microsphere.net.URLUtils; import io.microsphere.util.Utils; import java.io.File; @@ -30,6 +32,7 @@ import static io.microsphere.constants.SeparatorConstants.ARCHIVE_ENTRY_SEPARATOR; import static io.microsphere.io.IOUtils.close; import static io.microsphere.io.IOUtils.copy; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.net.URLUtils.decode; import static io.microsphere.net.URLUtils.normalizePath; import static io.microsphere.net.URLUtils.resolveArchiveFile; @@ -49,6 +52,8 @@ */ public abstract class JarUtils implements Utils { + private static final Logger logger = getLogger(URLUtils.class); + /** * The resource path of Manifest file in JAR archive. * Typically located under {@code META-INF/MANIFEST.MF} in standard Java archives. @@ -72,12 +77,21 @@ public abstract class JarUtils implements Utils { * * @param jarURL the URL pointing to a JAR file or entry; must not be {@code null} * @return a new {@link JarFile} instance if resolved successfully, or {@code null} if resolution fails - * @throws IOException if an I/O error occurs while creating the JAR file + * @throws IllegalArgumentException if the URL protocol is neither "jar" nor "file" */ @Nullable - public static JarFile toJarFile(URL jarURL) throws IOException { + public static JarFile toJarFile(URL jarURL) throws IllegalArgumentException { final String jarAbsolutePath = resolveJarAbsolutePath(jarURL); - return jarAbsolutePath == null ? null : new JarFile(jarAbsolutePath); + if (jarAbsolutePath == null) { + return null; + } + JarFile jarFile = null; + try { + jarFile = new JarFile(jarAbsolutePath); + } catch (IOException e) { + logger.trace("The JarFile can't be open from the url : {}", jarURL, e); + } + return jarFile; } /** @@ -210,10 +224,9 @@ protected static List doFilter(Iterable jarEntries, JarEntry * * @param jarURL the URL pointing to a resource within a JAR file; must not be {@code null} * @return the resolved {@link JarEntry} if found, or {@code null} if no such entry exists - * @throws IOException if an I/O error occurs while reading the JAR file or resolving the entry */ @Nullable - public static JarEntry findJarEntry(URL jarURL) throws IOException { + public static JarEntry findJarEntry(URL jarURL) { JarFile jarFile = toJarFile(jarURL); if (jarFile == null) { return null; @@ -385,7 +398,7 @@ public static void extract(URL jarResourceURL, File targetDirectory, JarEntryFil * @return {@code true} if the URL points to a directory entry in a JAR file, {@code false} otherwise * @throws IOException if an I/O error occurs while resolving the JAR file or entry */ - public static boolean isDirectoryEntry(URL url) throws IOException { + public static boolean isDirectoryEntry(URL url) { String protocol = url.getProtocol(); if (JAR_PROTOCOL.equals(protocol)) { final String relativePath = resolveRelativePath(url); From 3da35a8652019304b1fc7f6d10e8bdf35ba61aaa Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 13:39:43 +0800 Subject: [PATCH 146/438] Refactor isDirectoryURL, remove toJarFile method Simplify URL directory detection by adding an early null check, delegating JAR-entry checks to JarUtils.isDirectoryEntry, and using buildPath(...) for file URLs. The previous implementation that opened JarFile and resolved JarEntry has been removed (including the toJarFile helper and its Javadoc) to avoid exception-heavy logic and improve readability; related unused imports were also cleaned up. --- .../java/io/microsphere/net/URLUtils.java | 91 +++---------------- 1 file changed, 13 insertions(+), 78 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index 04390ae03..ca227a6e0 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -12,7 +12,6 @@ import io.microsphere.util.Utils; import java.io.File; -import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; @@ -25,7 +24,6 @@ import java.util.Map; import java.util.Set; import java.util.StringJoiner; -import java.util.jar.JarEntry; import java.util.jar.JarFile; import static io.microsphere.collection.CollectionUtils.size; @@ -64,7 +62,8 @@ import static io.microsphere.util.StringUtils.substringAfterLast; import static io.microsphere.util.SystemUtils.FILE_ENCODING; import static io.microsphere.util.SystemUtils.IS_OS_WINDOWS; -import static io.microsphere.util.jar.JarUtils.resolveRelativePath; +import static io.microsphere.util.jar.JarUtils.isDirectoryEntry; +import static io.microsphere.util.jar.JarUtils.toJarFile; import static java.lang.Character.isWhitespace; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; @@ -697,28 +696,18 @@ public static String decode(String value, String encoding) throws IllegalArgumen * @throws NullPointerException if the provided URL is {@code null} */ public static boolean isDirectoryURL(URL url) { - boolean isDirectory = false; - if (url != null) { - String protocol = url.getProtocol(); - try { - if (JAR_PROTOCOL.equals(protocol)) { - JarFile jarFile = toJarFile(url); // Test whether valid jar or not - final String relativePath = resolveRelativePath(url); - if (EMPTY.equals(relativePath)) { // root directory in jar - isDirectory = true; - } else { - JarEntry jarEntry = jarFile.getJarEntry(relativePath); - isDirectory = jarEntry != null && jarEntry.isDirectory(); - } - } else if (FILE_PROTOCOL.equals(protocol)) { - File classPathFile = new File(url.toURI()); - isDirectory = classPathFile.isDirectory(); - } - } catch (Exception e) { - isDirectory = false; - } + if (url == null) { + return false; + } + if (isDirectoryEntry(url)) { + return true; + } + String protocol = url.getProtocol(); + if (FILE_PROTOCOL.equals(protocol)) { + File classPathFile = new File(buildPath(url)); + return classPathFile.isDirectory(); } - return isDirectory; + return false; } /** @@ -814,60 +803,6 @@ public static boolean isArchiveURL(URL url) { return flag; } - /** - * Converts the provided URL to a {@link JarFile} instance if it refers to a valid JAR file. - * - *

    This method attempts to open the URL as a JAR file. If the URL uses the "file" protocol, - * it checks whether the corresponding file exists and is a valid JAR. For other protocols (e.g., "jar"), - * it tries to extract and open the underlying JAR file.

    - * - *

    Example Usage

    - *
    {@code
    -     * // Example 1: Valid JAR file URL
    -     * URL jarURL = new URL("file:/path/to/archive.jar");
    -     * JarFile jarFile = URLUtils.toJarFile(jarURL);
    -     * if (jarFile != null) {
    -     *     System.out.println("Successfully opened JAR file.");
    -     * } else {
    -     *     System.out.println("Failed to open JAR file.");
    -     * }
    -     *
    -     * // Example 2: Invalid JAR file URL
    -     * URL invalidURL = new URL("file:/path/to/invalid.jar");
    -     * jarFile = URLUtils.toJarFile(invalidURL);
    -     * System.out.println(jarFile == null); // Output: true
    -     *
    -     * // Example 3: URL with "jar" protocol
    -     * URL urlWithJarProtocol = new URL("jar:file:/path/to/archive.jar!/entry/path");
    -     * jarFile = URLUtils.toJarFile(urlWithJarProtocol);
    -     * System.out.println(jarFile != null); // Output: true
    -     * }
    - * - * @param url The URL to convert into a JAR file. - * @return A non-null {@link JarFile} if the URL refers to a valid JAR file; otherwise, returns {@code null}. - * @throws NullPointerException if the provided URL is {@code null}. - */ - @Nullable - public static JarFile toJarFile(URL url) { - String path = buildPath(url); - File file = new File(path); - if (!file.exists()) { - if (logger.isTraceEnabled()) { - logger.trace("The JarFile is not existed from the url : {}", url); - } - return null; - } - JarFile jarFile = null; - try { - jarFile = new JarFile(file); - } catch (IOException e) { - if (logger.isTraceEnabled()) { - logger.trace("The JarFile can't be open from the url : {}", url); - } - } - return jarFile; - } - /** * Builds a normalized URI path by concatenating the provided path segments with slashes. * From fc017fbe37a2083ea90ad3600dbe5464b91027bc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 13:39:52 +0800 Subject: [PATCH 147/438] Add null case to testIsDirectoryURL Update URLUtilsTest.testIsDirectoryURL to assert that isDirectoryURL(null) returns false and remove the unnecessary 'throws Exception' from the test signature. This strengthens the test by covering a null input and cleans up the method declaration. --- .../src/test/java/io/microsphere/net/URLUtilsTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java index 5947751cb..a98d06f78 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java @@ -297,7 +297,9 @@ void testDecodeOnInvalidEncoding() { } @Test - void testIsDirectoryURL() throws Exception { + void testIsDirectoryURL() { + assertFalse(isDirectoryURL(null)); + URL resourceURL = getClassResource(classLoader, StringUtils.class); assertFalse(isDirectoryURL(classFileURL)); From 4e1cba302d0900449244a59e2558b019a48e95af Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 22:10:38 +0800 Subject: [PATCH 148/438] Add archive protocol check, refactor URL helpers Introduce isArchiveProtocol(String) to centralize archive-protocol checks (jar/zip/war/ear) and update isArchiveURL to use it. Add ZIP_PROTOCOL import and non-null check via Objects.nonNull for JarFile. Reorganize and reintroduce URL path helper methods (resolvePath, resolvePathFromFile, resolvePathFromJar, buildPath) and adjust resolveArchiveDirectory to use buildPath(resourceURL). Replace manual list-first logic with ListUtils.first in getFirst and clean up related imports and javadoc for the new API. --- .../java/io/microsphere/net/URLUtils.java | 219 ++++++++++-------- 1 file changed, 123 insertions(+), 96 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index ca227a6e0..30c07b58d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -27,6 +27,7 @@ import java.util.jar.JarFile; import static io.microsphere.collection.CollectionUtils.size; +import static io.microsphere.collection.ListUtils.first; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.isEmpty; import static io.microsphere.collection.MapUtils.newFixedLinkedHashMap; @@ -38,6 +39,7 @@ import static io.microsphere.constants.ProtocolConstants.FILE_PROTOCOL; import static io.microsphere.constants.ProtocolConstants.JAR_PROTOCOL; import static io.microsphere.constants.ProtocolConstants.WAR_PROTOCOL; +import static io.microsphere.constants.ProtocolConstants.ZIP_PROTOCOL; import static io.microsphere.constants.SeparatorConstants.ARCHIVE_ENTRY_SEPARATOR; import static io.microsphere.constants.SymbolConstants.AND_CHAR; import static io.microsphere.constants.SymbolConstants.COLON; @@ -69,6 +71,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; +import static java.util.Objects.nonNull; /** * {@link URL} Utility class @@ -243,86 +246,6 @@ public static String resolveBasePath(URL url) throws NullPointerException { return resolvePath(url, false); } - static String resolvePath(URL url, boolean includeArchiveEntryPath) { - String protocol = url.getProtocol(); - - switch (protocol) { - case FILE_PROTOCOL: - return resolvePathFromFile(url); - case JAR_PROTOCOL: - return resolvePathFromJar(url, includeArchiveEntryPath); - } - - String path = url.getPath(); - - // path may contain the matrix parameters - int indexOfMatrixString = indexOfMatrixString(path); - // path removes the matrix parameters if present - path = indexOfMatrixString > -1 ? path.substring(0, indexOfMatrixString) : path; - - return path; - } - - static String resolvePathFromFile(URL url) { - return resolvePathFromFile(url, IS_OS_WINDOWS); - } - - static String resolvePathFromFile(URL url, boolean isOsWindows) { - String path = buildPath(url); - if (isOsWindows) { - path = substringAfter(path, SLASH); - } - return path; - } - - static String buildPath(URL url) { - String authority = url.getAuthority(); - String path = url.getPath(); - int length = 0; - int authorityLength = length(authority); - if (authorityLength > 0) { - length += authority.length() + 1; - } - int pathLength = length(path); - if (pathLength > 0) { - length += pathLength; - } - StringBuilder pathBuilder = new StringBuilder(length); - if (authorityLength > 0) { - pathBuilder.append(SLASH_CHAR) - .append(authority); - } - if (pathLength > 0) { - pathBuilder.append(path); - } - return pathBuilder.toString(); - } - - static String resolvePathFromJar(URL url, boolean includeArchiveEntryPath) { - String path = buildPath(url); - int filePrefixIndex = path.indexOf(FILE_URL_PREFIX); - if (filePrefixIndex == 0) { // path starts with "file:/" - path = path.substring(FILE_URL_PREFIX_LENGTH); - } - - // path removes the archive entry path if present - if (!includeArchiveEntryPath) { - int indexOfArchiveEntry = indexOfArchiveEntry(path); - path = indexOfArchiveEntry > -1 ? path.substring(0, indexOfArchiveEntry) : path; - } - - int indexOfColon = path.indexOf(COLON_CHAR); - if (indexOfColon == -1) { // the path on the Unix/Linux - // path adds the leading slash if not present - int indexOfSlash = path.indexOf(SLASH_CHAR); - if (indexOfSlash != 0) { - path = SLASH_CHAR + path; - } - } // else the path on the Windows - - return path; - } - /** * Resolves the archive file from the specified URL. * @@ -364,7 +287,7 @@ protected static File doResolveArchiveFile(URL url) throws NullPointerException } protected static File resolveArchiveDirectory(URL resourceURL) { - String resourcePath = new File(resourceURL.getFile()).toString(); + String resourcePath = buildPath(resourceURL); Set classPaths = getClassPaths(); File archiveDirectory = null; for (String classPath : classPaths) { @@ -745,7 +668,7 @@ public static boolean isJarURL(URL url) { boolean flag = false; if (FILE_PROTOCOL.equals(protocol)) { JarFile jarFile = toJarFile(url); - flag = jarFile != null; + flag = nonNull(jarFile); } else if (JAR_PROTOCOL.equals(protocol)) { flag = true; } @@ -788,19 +711,43 @@ public static boolean isJarURL(URL url) { * @throws NullPointerException if the provided URL is {@code null}. */ public static boolean isArchiveURL(URL url) { - String protocol = url.getProtocol(); - boolean flag = false; - switch (protocol) { - case JAR_PROTOCOL: - case WAR_PROTOCOL: - case EAR_PROTOCOL: - flag = true; - break; - case FILE_PROTOCOL: - JarFile jarFile = toJarFile(url); - flag = jarFile != null; + if (isJarURL(url)) { + return true; } - return flag; + String protocol = url.getProtocol(); + return isArchiveProtocol(protocol); + } + + /** + * Determines whether the specified protocol represents an archive type. + * + *

    This method checks if the given protocol matches known archive protocols such as "jar", "zip", "war", or "ear". + * These protocols are commonly used to reference compressed or packaged resources.

    + * + *

    Example Usage

    + *
    {@code
    +     * // Valid archive protocols
    +     * boolean result = URLUtils.isArchiveProtocol("jar");
    +     * System.out.println(result); // Output: true
    +     *
    +     * result = URLUtils.isArchiveProtocol("war");
    +     * System.out.println(result); // Output: true
    +     *
    +     * // Non-archive protocol
    +     * result = URLUtils.isArchiveProtocol("http");
    +     * System.out.println(result); // Output: false
    +     *
    +     * // Case-sensitive check
    +     * result = URLUtils.isArchiveProtocol("JAR");
    +     * System.out.println(result); // Output: false
    +     * }
    + * + * @param protocol The protocol string to check. + * @return {@code true} if the protocol is an archive type; otherwise, {@code false}. + */ + public static boolean isArchiveProtocol(String protocol) { + return JAR_PROTOCOL.equals(protocol) || ZIP_PROTOCOL.equals(protocol) || WAR_PROTOCOL.equals(protocol) + || EAR_PROTOCOL.equals(protocol); } /** @@ -1578,6 +1525,86 @@ public static void close(URLConnection conn) { } } + static String resolvePath(URL url, boolean includeArchiveEntryPath) { + String protocol = url.getProtocol(); + + switch (protocol) { + case FILE_PROTOCOL: + return resolvePathFromFile(url); + case JAR_PROTOCOL: + return resolvePathFromJar(url, includeArchiveEntryPath); + } + + String path = url.getPath(); + + // path may contain the matrix parameters + int indexOfMatrixString = indexOfMatrixString(path); + // path removes the matrix parameters if present + path = indexOfMatrixString > -1 ? path.substring(0, indexOfMatrixString) : path; + + return path; + } + + static String resolvePathFromFile(URL url) { + return resolvePathFromFile(url, IS_OS_WINDOWS); + } + + static String resolvePathFromFile(URL url, boolean isOsWindows) { + String path = buildPath(url); + if (isOsWindows) { + path = substringAfter(path, SLASH); + } + return path; + } + + static String buildPath(URL url) { + String authority = url.getAuthority(); + String path = url.getPath(); + int length = 0; + int authorityLength = length(authority); + if (authorityLength > 0) { + length += authority.length() + 1; + } + int pathLength = length(path); + if (pathLength > 0) { + length += pathLength; + } + StringBuilder pathBuilder = new StringBuilder(length); + if (authorityLength > 0) { + pathBuilder.append(SLASH_CHAR) + .append(authority); + } + if (pathLength > 0) { + pathBuilder.append(path); + } + return pathBuilder.toString(); + } + + static String resolvePathFromJar(URL url, boolean includeArchiveEntryPath) { + String path = buildPath(url); + int filePrefixIndex = path.indexOf(FILE_URL_PREFIX); + if (filePrefixIndex == 0) { // path starts with "file:/" + path = path.substring(FILE_URL_PREFIX_LENGTH); + } + + // path removes the archive entry path if present + if (!includeArchiveEntryPath) { + int indexOfArchiveEntry = indexOfArchiveEntry(path); + path = indexOfArchiveEntry > -1 ? path.substring(0, indexOfArchiveEntry) : path; + } + + int indexOfColon = path.indexOf(COLON_CHAR); + if (indexOfColon == -1) { // the path on the Unix/Linux + // path adds the leading slash if not present + int indexOfSlash = path.indexOf(SLASH_CHAR); + if (indexOfSlash != 0) { + path = SLASH_CHAR + path; + } + } // else the path on the Windows + + return path; + } + static String resolvePath(String value, int indexOfMatrixString) { return indexOfMatrixString > -1 ? value.substring(0, indexOfMatrixString) : value; } @@ -1703,7 +1730,7 @@ protected static String buildString(String name, String[] values, char separator protected static String getFirst(Map> parameters, String name) { List values = parameters.get(name); - return values == null || values.isEmpty() ? null : values.get(0); + return first(values); } private static int indexOfArchiveEntry(String path) { @@ -1712,4 +1739,4 @@ private static int indexOfArchiveEntry(String path) { private URLUtils() { } -} +} \ No newline at end of file From 5c4a7fdd80ba153641962e133a6764e18ec0ff2f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 13 Feb 2026 22:10:45 +0800 Subject: [PATCH 149/438] Enhance URLUtilsTest coverage and refactor Add and refactor tests in URLUtilsTest to improve coverage for archive-related behavior. Introduces new tests for unresolved archive files, various isJarURL edge cases (HTTP and null), and isArchiveProtocol checks for JAR/ZIP/WAR/EAR. Adjusts isDirectoryURL test to verify directory entries and uses JarUtils.isDirectoryEntry. Removes unnecessary throws declarations from many tests and simplifies helper usage (resolvePath, ofURL). Adds a test helper for testing archive protocols and leaves some additional archive tests commented for future use. --- .../java/io/microsphere/net/URLUtilsTest.java | 93 +++++++++++++++---- 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java index a98d06f78..0a240e302 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java @@ -25,10 +25,17 @@ import static io.microsphere.collection.MapUtils.newHashMap; import static io.microsphere.constants.FileConstants.CLASS_EXTENSION; import static io.microsphere.constants.PathConstants.SLASH; +import static io.microsphere.constants.PathConstants.SLASH_CHAR; +import static io.microsphere.constants.ProtocolConstants.EAR_PROTOCOL; +import static io.microsphere.constants.ProtocolConstants.FILE_PROTOCOL; import static io.microsphere.constants.ProtocolConstants.FTP_PROTOCOL; import static io.microsphere.constants.ProtocolConstants.HTTP_PROTOCOL; +import static io.microsphere.constants.ProtocolConstants.JAR_PROTOCOL; +import static io.microsphere.constants.ProtocolConstants.WAR_PROTOCOL; +import static io.microsphere.constants.ProtocolConstants.ZIP_PROTOCOL; import static io.microsphere.constants.SymbolConstants.AND_CHAR; import static io.microsphere.constants.SymbolConstants.COLON; +import static io.microsphere.constants.SymbolConstants.COLON_CHAR; import static io.microsphere.constants.SymbolConstants.SPACE; import static io.microsphere.net.URLUtils.FILE_URL_PREFIX; import static io.microsphere.net.URLUtils.attachURLStreamHandlerFactory; @@ -42,6 +49,7 @@ import static io.microsphere.net.URLUtils.getMutableURLStreamHandlerFactory; import static io.microsphere.net.URLUtils.getSubProtocol; import static io.microsphere.net.URLUtils.getURLStreamHandlerFactory; +import static io.microsphere.net.URLUtils.isArchiveProtocol; import static io.microsphere.net.URLUtils.isArchiveURL; import static io.microsphere.net.URLUtils.isDirectoryURL; import static io.microsphere.net.URLUtils.isJarURL; @@ -60,12 +68,14 @@ import static io.microsphere.net.URLUtils.resolveSubProtocols; import static io.microsphere.net.URLUtils.toExternalForm; import static io.microsphere.net.console.HandlerTest.TEST_CONSOLE_URL; +import static io.microsphere.util.ClassLoaderUtils.ResourceType.PACKAGE; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; import static io.microsphere.util.ClassLoaderUtils.getClassResource; import static io.microsphere.util.ClassLoaderUtils.getResource; import static io.microsphere.util.StringUtils.EMPTY_STRING; import static io.microsphere.util.StringUtils.substringBeforeLast; import static io.microsphere.util.SystemUtils.USER_DIR; +import static io.microsphere.util.jar.JarUtils.isDirectoryEntry; import static java.nio.file.Paths.get; import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -160,7 +170,7 @@ void testResolveBasePathOnFile() { } @Test - void testResolveBasePathOnArchiveEntry() throws MalformedURLException { + void testResolveBasePathOnArchiveEntry() { String basePath = resolveBasePath(classArchiveEntryURL); assertNotNull(basePath); assertTrue(new File(basePath).exists()); @@ -178,6 +188,12 @@ void testResolveArchiveFileOnClassFile() { assertTrue(archiveFile.exists()); } + @Test + void testResolveArchiveFileOnNotFoundJar() { + URL url = ofURL("jar:file:/not-found.jar!/com/acme"); + assertNull(resolveArchiveFile(url)); + } + @Test void testResolveQueryParameters() { String url = TEST_HTTP_WITH_QUERY_STRING; @@ -300,9 +316,14 @@ void testDecodeOnInvalidEncoding() { void testIsDirectoryURL() { assertFalse(isDirectoryURL(null)); - URL resourceURL = getClassResource(classLoader, StringUtils.class); assertFalse(isDirectoryURL(classFileURL)); + URL resourceURL = getClassResource(classLoader, StringUtils.class); + assertFalse(isDirectoryURL(resourceURL)); + + resourceURL = getResource(this.classLoader, PACKAGE, "javax.annotation"); + assertTrue(isDirectoryEntry(resourceURL)); + String externalForm = null; externalForm = substringBeforeLast(resourceURL.toExternalForm(), StringUtils.class.getSimpleName() + CLASS_EXTENSION); resourceURL = ofURL(externalForm); @@ -335,27 +356,57 @@ void testIsJarURLOnArchiveEntry() { } @Test - void testIsJarURLOnArchiveFile() throws MalformedURLException { + void testIsJarURLOnArchiveFile() { File archiveFile = resolveArchiveFile(classArchiveEntryURL); assertTrue(isJarURL(ofURL(FILE_URL_PREFIX + archiveFile.getAbsolutePath()))); } @Test - void testIsArchiveURLOnJar() throws MalformedURLException { + void testIsJarURLOnHttp() { + URL url = ofURL("http://localhost"); + assertFalse(isJarURL(url)); + } + + @Test + void testIsJarURLOnNPE() { + assertThrows(NullPointerException.class, () -> isJarURL(null)); + } + + @Test + void testIsArchiveURLOnJar() { assertTrue(isArchiveURL(classArchiveEntryURL)); } +// +// @Test +// void testIsArchiveURLOnWar() { +// testIsArchiveURL(WAR_PROTOCOL); +// } +// +// @Test +// void testIsArchiveURLOnEar() { +// testIsArchiveURL(EAR_PROTOCOL); +// } @Test - void testIsArchiveURLOnFile() throws MalformedURLException { - File archiveFile = resolveArchiveFile(classArchiveEntryURL); - assertTrue(isArchiveURL(ofURL(FILE_URL_PREFIX + archiveFile.getAbsolutePath()))); + void testIsArchiveURLOnFile() { + testIsArchiveURL(FILE_PROTOCOL); } @Test - void testIsArchiveURLOnNotJar() throws MalformedURLException { + void testIsArchiveURLOnNotJar() { assertFalse(isArchiveURL(classFileURL)); } + @Test + void testIsArchiveProtocol() { + assertTrue(isArchiveProtocol(JAR_PROTOCOL)); + assertTrue(isArchiveProtocol(ZIP_PROTOCOL)); + assertTrue(isArchiveProtocol(WAR_PROTOCOL)); + assertTrue(isArchiveProtocol(EAR_PROTOCOL)); + assertFalse(isArchiveProtocol(FTP_PROTOCOL)); + assertFalse(isArchiveProtocol(FILE_PROTOCOL)); + } + @Test void testBuildURI() { String path = buildURI("a", "b", "c"); @@ -409,26 +460,26 @@ void testToExternalFormOnClassArchiveEntry() { } @Test - void testToExternalFormWithMatrixString() throws MalformedURLException { + void testToExternalFormWithMatrixString() { testToExternalForm(TEST_HTTP_WITH_MATRIX_STRING); } @Test - void testToExternalFormWithQueryString() throws MalformedURLException { + void testToExternalFormWithQueryString() { testToExternalForm(TEST_HTTP_WITH_QUERY_STRING); } @Test - void testToExternalFormWithPath() throws MalformedURLException { + void testToExternalFormWithPath() { testToExternalForm(TEST_HTTP_WITH_PATH); } @Test - void testToExternalFormWithRef() throws MalformedURLException { + void testToExternalFormWithRef() { testToExternalForm(TEST_HTTP_WITH_PATH_HASH); } - private void testToExternalForm(String urlString) throws MalformedURLException { + private void testToExternalForm(String urlString) { testToExternalForm(ofURL(urlString)); } @@ -447,7 +498,7 @@ void testGetSubProtocolWithSubProtocol() { } @Test - void testResolveSubProtocolsFromMatrixString() throws MalformedURLException { + void testResolveSubProtocolsFromMatrixString() { URL url = ofURL(TEST_HTTP_WITH_SP_MATRIX); List subProtocols = resolveSubProtocols(url); assertEquals(2, subProtocols.size()); @@ -456,7 +507,7 @@ void testResolveSubProtocolsFromMatrixString() throws MalformedURLException { } @Test - void testResolveSubProtocolsFromProtocol() throws MalformedURLException { + void testResolveSubProtocolsFromProtocol() { new Handler(); URL url = ofURL("console:text:properties://localhost"); List subProtocols = resolveSubProtocols(url); @@ -485,6 +536,11 @@ void testResolvePath() { assertResolvePath(classPathURL); assertResolvePath(classFileURL); } +// +// @Test +// void testResolvePathWith() { +// assertResolvePath(classArchiveEntryURL, false); +// } @Test void testResolvePathWithMatrixString() { @@ -607,8 +663,13 @@ private void assertResolvePath(URL url) { } private void assertResolvePath(URL url, boolean includeArchiveEntryPath) { - String path = URLUtils.resolvePath(url, includeArchiveEntryPath); + String path = resolvePath(url, includeArchiveEntryPath); assertEquals(new File(path), new File(url.getPath())); } + void testIsArchiveURL(String protocol) { + File archiveFile = resolveArchiveFile(classArchiveEntryURL); + assertTrue(isArchiveURL(ofURL(protocol + COLON_CHAR + SLASH_CHAR + archiveFile.getAbsolutePath()))); + } + } From 6ce36cf7789a114c72a4675354e0051cd31cd35f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 14 Feb 2026 21:38:55 +0800 Subject: [PATCH 150/438] Make cache package-private and clear it in tests Relax visibility of unfinishedProcessesCache from private to package-private so tests can access it. Add a @BeforeEach setUp in ProcessManagerTest (and import BeforeEach) to clear the cache before each test, ensuring test isolation and preventing state leakage between tests. --- .../main/java/io/microsphere/process/ProcessManager.java | 2 +- .../java/io/microsphere/process/ProcessManagerTest.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessManager.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessManager.java index 67a6b4930..55f41d857 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessManager.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessManager.java @@ -44,7 +44,7 @@ public class ProcessManager { */ public static final ProcessManager INSTANCE = new ProcessManager(); - private final ConcurrentMap unfinishedProcessesCache = new ConcurrentHashMap<>(); + final ConcurrentMap unfinishedProcessesCache = new ConcurrentHashMap<>(); private ProcessManager() { } diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java index 9ad660ff3..445ecffc3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java @@ -1,6 +1,7 @@ package io.microsphere.process; import io.microsphere.AbstractTestCase; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; @@ -24,6 +25,11 @@ */ class ProcessManagerTest extends AbstractTestCase { + @BeforeEach + void setUp() { + INSTANCE.unfinishedProcessesCache.clear(); + } + @Test void test() { ProcessManager processManager = INSTANCE; From 51ed27e6bf2db4ef757a27bed056d4db184c8293 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 01:35:03 +0000 Subject: [PATCH 151/438] Bump junit.version from 6.0.2 to 6.0.3 Bumps `junit.version` from 6.0.2 to 6.0.3. Updates `org.junit.jupiter:junit-jupiter` from 6.0.2 to 6.0.3 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r6.0.2...r6.0.3) Updates `org.junit.jupiter:junit-jupiter-engine` from 6.0.2 to 6.0.3 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r6.0.2...r6.0.3) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 23460ca6c..c01f3a982 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -26,7 +26,7 @@ 2.0.17 1.5.24 - 6.0.2 + 6.0.3 1.37 From 41caea728578641b4c8bd93ce1160a5467167a86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 01:33:54 +0000 Subject: [PATCH 152/438] Bump ch.qos.logback:logback-classic from 1.5.24 to 1.5.32 Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.24 to 1.5.32. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.5.24...v_1.5.32) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-version: 1.5.32 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 23460ca6c..d66c3ff48 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -24,7 +24,7 @@ 1.3.2 7.0.3 2.0.17 - 1.5.24 + 1.5.32 6.0.2 1.37 From 1ea78ae02115f1590f029be94b67f6509a40c9dc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 16:50:21 +0800 Subject: [PATCH 153/438] Refactor URLUtils path parsing and handler lookup Simplify URLUtils internals by switching to shared String utilities for matrix/archive parsing and trimming. Replaced manual index-based logic with substringBefore/After helpers, removed redundant indexOfMatrixString/indexOfArchiveEntry helpers, and consolidated path truncation into truncateMatrixString. Made MutableURLStreamHandlerFactory finder package-visible and use ClassUtils.cast for safer casting. Minor cleanups: added/imported SEMICOLON and other utils, simplified a trace log call, and removed unused helper methods to reduce duplication and improve clarity. --- .../java/io/microsphere/net/URLUtils.java | 54 +++++-------------- 1 file changed, 14 insertions(+), 40 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index 30c07b58d..54cc411d4 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -47,6 +47,7 @@ import static io.microsphere.constants.SymbolConstants.EQUAL_CHAR; import static io.microsphere.constants.SymbolConstants.QUERY_STRING; import static io.microsphere.constants.SymbolConstants.QUERY_STRING_CHAR; +import static io.microsphere.constants.SymbolConstants.SEMICOLON; import static io.microsphere.constants.SymbolConstants.SEMICOLON_CHAR; import static io.microsphere.constants.SymbolConstants.SHARP_CHAR; import static io.microsphere.logging.LoggerFactory.getLogger; @@ -55,6 +56,7 @@ import static io.microsphere.util.ArrayUtils.length; import static io.microsphere.util.CharSequenceUtils.length; import static io.microsphere.util.ClassPathUtils.getClassPaths; +import static io.microsphere.util.ClassUtils.cast; import static io.microsphere.util.StringUtils.EMPTY; import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; import static io.microsphere.util.StringUtils.isBlank; @@ -62,6 +64,7 @@ import static io.microsphere.util.StringUtils.split; import static io.microsphere.util.StringUtils.substringAfter; import static io.microsphere.util.StringUtils.substringAfterLast; +import static io.microsphere.util.StringUtils.substringBefore; import static io.microsphere.util.SystemUtils.FILE_ENCODING; import static io.microsphere.util.SystemUtils.IS_OS_WINDOWS; import static io.microsphere.util.jar.JarUtils.isDirectoryEntry; @@ -210,12 +213,8 @@ public static String resolveArchiveEntryPath(URL archiveFileURL) throws NullPoin } protected static String doResolveArchiveEntryPath(String path) { - int beginIndex = indexOfArchiveEntry(path); - if (beginIndex > -1) { - String relativePath = path.substring(beginIndex + ARCHIVE_ENTRY_SEPARATOR_LENGTH); - return decode(relativePath); - } - return null; + String archiveEntryPath = substringAfter(path, ARCHIVE_ENTRY_SEPARATOR); + return EMPTY == archiveEntryPath ? null : decode(archiveEntryPath); } /** @@ -953,14 +952,14 @@ public static String toExternalForm(URL url) throws NullPointerException { } if (hasPath) { - int indexOfMatrixString = indexOfMatrixString(path); + int indexOfMatrixString = path.indexOf(SEMICOLON_CHAR); if (indexOfMatrixString > -1) { hasMatrix = true; Map> matrixParameters = resolveMatrixParameters(path); List subProtocols = matrixParameters.getOrDefault(SUB_PROTOCOL_MATRIX_NAME, emptyList()); protocol = reformProtocol(protocol, subProtocols); matrix = buildMatrixString(matrixParameters); - path = resolvePath(path, indexOfMatrixString); + path = path.substring(0, indexOfMatrixString); } } @@ -1185,9 +1184,7 @@ public static String resolveProtocol(String url) { } for (int i = 0; i <= indexOfColon; i++) { if (isWhitespace(url.charAt(i))) { - if (logger.isTraceEnabled()) { - logger.trace("The protocol content should not contain the whitespace[url : '{}' , index : {}]", url, i); - } + logger.trace("The protocol content should not contain the whitespace[url : '{}' , index : {}]", url, i); return null; } } @@ -1535,14 +1532,7 @@ static String resolvePath(URL url, boolean includeArchiveEntryPath) { return resolvePathFromJar(url, includeArchiveEntryPath); } - String path = url.getPath(); - - // path may contain the matrix parameters - int indexOfMatrixString = indexOfMatrixString(path); - // path removes the matrix parameters if present - path = indexOfMatrixString > -1 ? path.substring(0, indexOfMatrixString) : path; - - return path; + return truncateMatrixString(url.getPath()); } static String resolvePathFromFile(URL url) { @@ -1589,8 +1579,7 @@ static String resolvePathFromJar(URL url, boolean includeArchiveEntryPath) { // path removes the archive entry path if present if (!includeArchiveEntryPath) { - int indexOfArchiveEntry = indexOfArchiveEntry(path); - path = indexOfArchiveEntry > -1 ? path.substring(0, indexOfArchiveEntry) : path; + path = substringBefore(path, ARCHIVE_ENTRY_SEPARATOR); } int indexOfColon = path.indexOf(COLON_CHAR); @@ -1605,17 +1594,9 @@ static String resolvePathFromJar(URL url, boolean includeArchiveEntryPath) { return path; } - static String resolvePath(String value, int indexOfMatrixString) { - return indexOfMatrixString > -1 ? value.substring(0, indexOfMatrixString) : value; - } - protected static String truncateMatrixString(String value) { - int lastIndex = indexOfMatrixString(value); - return lastIndex > -1 ? value.substring(0, lastIndex) : value; - } - - protected static int indexOfMatrixString(String value) { - return value == null ? -1 : value.indexOf(SEMICOLON_CHAR); + String str = substringAfterLast(value, SEMICOLON); + return str == EMPTY ? value : str; } protected static MutableURLStreamHandlerFactory getMutableURLStreamHandlerFactory() { @@ -1634,7 +1615,7 @@ protected static MutableURLStreamHandlerFactory getMutableURLStreamHandlerFactor return factory; } - private static MutableURLStreamHandlerFactory findMutableURLStreamHandlerFactory(CompositeURLStreamHandlerFactory compositeFactory) { + static MutableURLStreamHandlerFactory findMutableURLStreamHandlerFactory(CompositeURLStreamHandlerFactory compositeFactory) { MutableURLStreamHandlerFactory target = null; for (URLStreamHandlerFactory factory : compositeFactory.getFactories()) { target = findMutableURLStreamHandlerFactory(factory); @@ -1646,10 +1627,7 @@ private static MutableURLStreamHandlerFactory findMutableURLStreamHandlerFactory } private static MutableURLStreamHandlerFactory findMutableURLStreamHandlerFactory(URLStreamHandlerFactory factory) { - if (factory instanceof MutableURLStreamHandlerFactory) { - return (MutableURLStreamHandlerFactory) factory; - } - return null; + return cast(factory, MutableURLStreamHandlerFactory.class); } protected static void clearURLStreamHandlerFactory() { @@ -1733,10 +1711,6 @@ protected static String getFirst(Map> parameters, String na return first(values); } - private static int indexOfArchiveEntry(String path) { - return path.indexOf(ARCHIVE_ENTRY_SEPARATOR); - } - private URLUtils() { } } \ No newline at end of file From 1b32ee9d1e546ff0f020742dad577343ece3392d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 16:50:34 +0800 Subject: [PATCH 154/438] Add URLUtils unit tests and imports Add several unit tests for URLUtils behavior and bring in needed static imports. New tests cover attachURLStreamHandlerFactory null handling, findMutableURLStreamHandlerFactory, resolveParameters parsing, resolvePathFromJar, and buildString null cases. Added static imports (ofMap, buildString, findMutableURLStreamHandlerFactory, resolvePathFromJar, EMPTY_STRING_ARRAY) and removed an old commented-out test. These changes improve test coverage for edge cases and null handling in URL utilities. --- .../java/io/microsphere/net/URLUtilsTest.java | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java index 0a240e302..136b6cd97 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java @@ -23,6 +23,7 @@ import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.newHashMap; +import static io.microsphere.collection.MapUtils.ofMap; import static io.microsphere.constants.FileConstants.CLASS_EXTENSION; import static io.microsphere.constants.PathConstants.SLASH; import static io.microsphere.constants.PathConstants.SLASH_CHAR; @@ -41,11 +42,13 @@ import static io.microsphere.net.URLUtils.attachURLStreamHandlerFactory; import static io.microsphere.net.URLUtils.buildMatrixString; import static io.microsphere.net.URLUtils.buildPath; +import static io.microsphere.net.URLUtils.buildString; import static io.microsphere.net.URLUtils.buildURI; import static io.microsphere.net.URLUtils.clearURLStreamHandlerFactory; import static io.microsphere.net.URLUtils.close; import static io.microsphere.net.URLUtils.decode; import static io.microsphere.net.URLUtils.encode; +import static io.microsphere.net.URLUtils.findMutableURLStreamHandlerFactory; import static io.microsphere.net.URLUtils.getMutableURLStreamHandlerFactory; import static io.microsphere.net.URLUtils.getSubProtocol; import static io.microsphere.net.URLUtils.getURLStreamHandlerFactory; @@ -63,6 +66,7 @@ import static io.microsphere.net.URLUtils.resolveParameters; import static io.microsphere.net.URLUtils.resolvePath; import static io.microsphere.net.URLUtils.resolvePathFromFile; +import static io.microsphere.net.URLUtils.resolvePathFromJar; import static io.microsphere.net.URLUtils.resolveProtocol; import static io.microsphere.net.URLUtils.resolveQueryParameters; import static io.microsphere.net.URLUtils.resolveSubProtocols; @@ -73,6 +77,7 @@ import static io.microsphere.util.ClassLoaderUtils.getClassResource; import static io.microsphere.util.ClassLoaderUtils.getResource; import static io.microsphere.util.StringUtils.EMPTY_STRING; +import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; import static io.microsphere.util.StringUtils.substringBeforeLast; import static io.microsphere.util.SystemUtils.USER_DIR; import static io.microsphere.util.jar.JarUtils.isDirectoryEntry; @@ -536,11 +541,6 @@ void testResolvePath() { assertResolvePath(classPathURL); assertResolvePath(classFileURL); } -// -// @Test -// void testResolvePathWith() { -// assertResolvePath(classArchiveEntryURL, false); -// } @Test void testResolvePathWithMatrixString() { @@ -561,6 +561,12 @@ void testAttachURLStreamHandlerFactory() { assertEquals(CompositeURLStreamHandlerFactory.class, compositeFactory.getClass()); } + @Test + void testAttachURLStreamHandlerFactoryOnNull() { + attachURLStreamHandlerFactory(null); + assertNull(getURLStreamHandlerFactory()); + } + @Test void testGetURLStreamHandlerFactory() { assertNull(getURLStreamHandlerFactory()); @@ -612,6 +618,12 @@ void testGetMutableURLStreamHandlerFactoryOnCreateIfAbsent() { assertNotNull(factory); } + @Test + void testFindMutableURLStreamHandlerFactory() { + CompositeURLStreamHandlerFactory compositeFactory = new CompositeURLStreamHandlerFactory(); + assertNull(findMutableURLStreamHandlerFactory(compositeFactory)); + } + @Test void testClearURLStreamHandlerFactory() { testGetMutableURLStreamHandlerFactoryFromAttached(); @@ -619,6 +631,12 @@ void testClearURLStreamHandlerFactory() { testGetMutableURLStreamHandlerFactory(); } + @Test + void testResolveParameters() { + assertEquals(ofMap("a", ofList("1"), "b", ofList("2")), resolveParameters("a=1&b=2", AND_CHAR)); + assertEquals(ofMap("a", ofList(""), "b", ofList("2")), resolveParameters("a&b=2", AND_CHAR)); + } + @Test void testResolveParametersOnNull() { assertSame(emptyMap(), resolveParameters(null, AND_CHAR)); @@ -629,6 +647,13 @@ void testResolveParametersOnEmptyString() { assertSame(emptyMap(), resolveParameters(EMPTY_STRING, AND_CHAR)); } + @Test + void testResolvePathFromJar() { + URL url = ofURL("http://acme.com:8080/x.jar"); + String path = resolvePathFromJar(url, true); + assertEquals("/acme.com:8080/x.jar", path); + } + @Test void testResolvePathFromFile() { URL url = ofURL("file:///D:/test"); @@ -658,6 +683,12 @@ void testBuildPath() { assertEquals(url.getPath(), path); } + @Test + void testBuildString() { + assertNull(buildString(null, EMPTY_STRING_ARRAY, AND_CHAR, COLON_CHAR)); + assertNull(buildString(null, null, AND_CHAR, COLON_CHAR)); + } + private void assertResolvePath(URL url) { assertResolvePath(url, true); } From 703d1601c5b87a236c06c8bbe8e168e393ef2327 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 16:56:37 +0800 Subject: [PATCH 155/438] Use substringBefore to truncate matrix string Fix truncateMatrixString to return the portion before matrix parameters. Previously it used substringAfterLast(value, SEMICOLON), which returned the text after the last ';'. This change uses substringBefore(value, SEMICOLON) so the method correctly returns the path segment before the first semicolon (or the original value if no semicolon is present). --- .../src/main/java/io/microsphere/net/URLUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index 54cc411d4..29d863a54 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -1595,7 +1595,7 @@ static String resolvePathFromJar(URL url, boolean includeArchiveEntryPath) { } protected static String truncateMatrixString(String value) { - String str = substringAfterLast(value, SEMICOLON); + String str = substringBefore(value, SEMICOLON); return str == EMPTY ? value : str; } From 0bebcb6ba85e46cc1e9300776207a295f613182f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 17:19:40 +0800 Subject: [PATCH 156/438] Add resolvePathFromJar test for file URL Extend URLUtilsTest to cover file protocol handling in resolvePathFromJar. Adds an assertion that resolving 'file:/dir/x.jar' with includeHost=false returns '/dir/x.jar', ensuring correct path extraction for file URLs. --- .../src/test/java/io/microsphere/net/URLUtilsTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java index 136b6cd97..f0a643019 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java @@ -652,6 +652,10 @@ void testResolvePathFromJar() { URL url = ofURL("http://acme.com:8080/x.jar"); String path = resolvePathFromJar(url, true); assertEquals("/acme.com:8080/x.jar", path); + + url = ofURL("file:/dir/x.jar"); + path = resolvePathFromJar(url, false); + assertEquals("/dir/x.jar", path); } @Test From 11ad7707d296e78b1a6044012e227f99db74c6f4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 17:27:34 +0800 Subject: [PATCH 157/438] Add truncateMatrixString tests Add unit tests for URLUtils.truncateMatrixString and extend an existing handler-factory test. The change imports truncateMatrixString and adds testTruncateMatrixString to verify matrix parameters are removed from URLs; it also adds a StandardURLStreamHandlerFactory to the composite factory in testFindMutableURLStreamHandlerFactory to ensure the utility still returns null in that scenario. --- .../src/test/java/io/microsphere/net/URLUtilsTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java index f0a643019..1f974e2d8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java @@ -71,6 +71,7 @@ import static io.microsphere.net.URLUtils.resolveQueryParameters; import static io.microsphere.net.URLUtils.resolveSubProtocols; import static io.microsphere.net.URLUtils.toExternalForm; +import static io.microsphere.net.URLUtils.truncateMatrixString; import static io.microsphere.net.console.HandlerTest.TEST_CONSOLE_URL; import static io.microsphere.util.ClassLoaderUtils.ResourceType.PACKAGE; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; @@ -622,6 +623,9 @@ void testGetMutableURLStreamHandlerFactoryOnCreateIfAbsent() { void testFindMutableURLStreamHandlerFactory() { CompositeURLStreamHandlerFactory compositeFactory = new CompositeURLStreamHandlerFactory(); assertNull(findMutableURLStreamHandlerFactory(compositeFactory)); + + compositeFactory.addURLStreamHandlerFactory(new StandardURLStreamHandlerFactory()); + assertNull(findMutableURLStreamHandlerFactory(compositeFactory)); } @Test @@ -676,6 +680,12 @@ void testResolvePathFromFile() { assertEquals("/D:/test", resolvePathFromFile(url, false)); } + @Test + void testTruncateMatrixString() { + assertEquals(TEST_HTTP_BASE, truncateMatrixString(TEST_HTTP_WITH_SP_MATRIX)); + assertEquals(TEST_HTTP_BASE, truncateMatrixString(TEST_HTTP_BASE)); + } + @Test void testBuildPath() { URL url = ofURL("file:///D:/test"); From 8f27eac330c0b7f87f83b3cb0e86c21719026660 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 17:31:58 +0800 Subject: [PATCH 158/438] Simplify truncateMatrixString return logic Remove the special-case check and return substringBefore(value, ';') directly. This makes the method consistently return the substring result and fixes the previous behavior where a leading ';' could cause the original value to be returned instead of an empty string. --- .../src/main/java/io/microsphere/net/URLUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index 29d863a54..c1701981d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -1595,8 +1595,7 @@ static String resolvePathFromJar(URL url, boolean includeArchiveEntryPath) { } protected static String truncateMatrixString(String value) { - String str = substringBefore(value, SEMICOLON); - return str == EMPTY ? value : str; + return substringBefore(value, SEMICOLON); } protected static MutableURLStreamHandlerFactory getMutableURLStreamHandlerFactory() { From c652af24dab844ca785fc17ff0f2cf0300f51e44 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 18:03:49 +0800 Subject: [PATCH 159/438] Refactor LookupKey.equals and simplify logging Improve LookupKey.equals by using instanceof (with explicit braces) and tidying the comparison/formatting. Remove the redundant logger.isWarnEnabled() guard in handleInvokeExactFailure so the warning is logged directly. Minor whitespace/formatting cleanups. --- .../io/microsphere/invoke/MethodHandleUtils.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java index 13ec7877e..ac7cb7552 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java @@ -249,6 +249,7 @@ public static int getModes(LookupMode... lookupModes) { } static class LookupKey { + final Class requestedClass; final int allowedModes; @@ -260,10 +261,15 @@ static class LookupKey { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (!(o instanceof LookupKey)) { + return false; + } LookupKey lookupKey = (LookupKey) o; - return allowedModes == lookupKey.allowedModes && Objects.equals(requestedClass, lookupKey.requestedClass); + return allowedModes == lookupKey.allowedModes && + Objects.equals(requestedClass, lookupKey.requestedClass); } @Override @@ -332,9 +338,7 @@ public static MethodHandle findStatic(Class requestedClass, String methodName * @param args the arguments of {@link MethodHandle#invokeExact(Object...)} */ public static void handleInvokeExactFailure(Throwable e, MethodHandle methodHandle, Object... args) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to invokeExact on the {} with arguments : {}", methodHandle, arrayToString(args), e); - } + logger.warn("Failed to invokeExact on the {} with arguments : {}", methodHandle, arrayToString(args), e); } protected static MethodHandle find(Class requestedClass, String methodName, Class[] parameterTypes, From d5cfa20b0011c35fc68b078c460eceb851260253 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 18:03:57 +0800 Subject: [PATCH 160/438] Add LookupKey equals/hashCode tests Add unit tests for MethodHandleUtils.LookupKey equality and hashCode behavior. The new tests verify equals against null and other types, inequality for different target classes and lookup modes, reflexivity and equality for identical keys, and consistent hashCode for equal keys. Also add the required static imports and assertions used by the tests. --- .../invoke/MethodHandleUtilsTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/invoke/MethodHandleUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/invoke/MethodHandleUtilsTest.java index 726dde298..d7765d440 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/invoke/MethodHandleUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/invoke/MethodHandleUtilsTest.java @@ -16,17 +16,24 @@ */ package io.microsphere.invoke; +import io.microsphere.invoke.MethodHandleUtils.LookupKey; import org.junit.jupiter.api.Test; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import static io.microsphere.invoke.MethodHandleUtils.LookupKey.buildKey; +import static io.microsphere.invoke.MethodHandleUtils.LookupMode.PROTECTED; +import static io.microsphere.invoke.MethodHandleUtils.LookupMode.PUBLIC; +import static io.microsphere.invoke.MethodHandleUtils.LookupMode.getModes; import static io.microsphere.invoke.MethodHandleUtils.findStatic; import static io.microsphere.invoke.MethodHandleUtils.findVirtual; import static io.microsphere.invoke.MethodHandleUtils.handleInvokeExactFailure; import static io.microsphere.invoke.MethodHandleUtils.lookup; import static io.microsphere.invoke.MethodHandlesLookupUtils.NOT_FOUND_METHOD_HANDLE; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertSame; /** @@ -96,6 +103,26 @@ void testHandleInvokeExactFailure() { handleInvokeExactFailure(new Throwable("testing"), methodHandle); } + @Test + void testLookupKeyEquals() { + LookupKey key = buildKey(getClass(), getModes(PUBLIC)); + assertFalse(key.equals(null)); + assertFalse(key.equals(this)); + + assertNotEquals(key, buildKey(String.class, getModes(PUBLIC))); + assertNotEquals(key, buildKey(getClass(), getModes(PROTECTED))); + + assertEquals(key, key); + assertEquals(key, buildKey(getClass(), getModes(PUBLIC))); + } + + @Test + void testLookupKeyHashCode() { + LookupKey key = buildKey(getClass(), getModes(PUBLIC)); + LookupKey key2 = buildKey(getClass(), getModes(PUBLIC)); + assertEquals(key.hashCode(), key2.hashCode()); + } + private void testFindVirtual(String methodName) throws Throwable { MethodHandle methodHandle = findVirtual(MethodHandleUtilsTest.class, methodName); assertEquals(methodName, (String) methodHandle.invokeExact(this)); From 101668a0f00617691e1dddfcaa73ba270f47eed6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 18:47:05 +0800 Subject: [PATCH 161/438] Remove isDebugEnabled check before debug log Simplify LoggingFileChangedListener by calling logger.debug(event.toString()) unconditionally instead of wrapping it in an isDebugEnabled() guard. This reduces code verbosity and relies on the logger implementation to ignore the call when debug logging is disabled. --- .../io/microsphere/io/event/LoggingFileChangedListener.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/event/LoggingFileChangedListener.java b/microsphere-java-core/src/main/java/io/microsphere/io/event/LoggingFileChangedListener.java index e104e361d..50077a7d5 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/event/LoggingFileChangedListener.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/event/LoggingFileChangedListener.java @@ -67,8 +67,6 @@ public void onFileDeleted(FileChangedEvent event) { } private void log(FileChangedEvent event) { - if (logger.isDebugEnabled()) { - logger.debug(event.toString()); - } + logger.debug(event.toString()); } } From 8ed254294e168c7e9407ff6f99adb526829dd91e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 18:47:11 +0800 Subject: [PATCH 162/438] Add LoggingFileChangedListenerTest Introduce LoggingFileChangedListenerTest unit tests for LoggingFileChangedListener. The tests create temporary files and verify onFileCreated, onFileModified, and onFileDeleted preserve the event's file and kind (using assertions on event.getFile() and event.getKind()). --- .../event/LoggingFileChangedListenerTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/io/event/LoggingFileChangedListenerTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/event/LoggingFileChangedListenerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/event/LoggingFileChangedListenerTest.java new file mode 100644 index 000000000..161c4eb6a --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/event/LoggingFileChangedListenerTest.java @@ -0,0 +1,75 @@ +/* + * 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. + */ + +package io.microsphere.io.event; + + +import io.microsphere.AbstractTestCase; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; + +import static io.microsphere.io.event.FileChangedEvent.Kind.CREATED; +import static io.microsphere.io.event.FileChangedEvent.Kind.DELETED; +import static io.microsphere.io.event.FileChangedEvent.Kind.MODIFIED; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link LoggingFileChangedListener} Test + * + * @author Mercy + * @see LoggingFileChangedListener + * @since 1.0.0 + */ +class LoggingFileChangedListenerTest extends AbstractTestCase { + + private LoggingFileChangedListener listener; + + @BeforeEach + void setUp() { + this.listener = new LoggingFileChangedListener(); + } + + @Test + void testOnFileCreated() throws IOException { + File file = createRandomTempFile(); + FileChangedEvent event = new FileChangedEvent(file, CREATED); + listener.onFileCreated(event); + assertSame(file, event.getFile()); + assertSame(CREATED, event.getKind()); + } + + @Test + void testOnFileModified() throws IOException { + File file = createRandomTempFile(); + FileChangedEvent event = new FileChangedEvent(file, MODIFIED); + listener.onFileModified(event); + assertSame(file, event.getFile()); + assertSame(MODIFIED, event.getKind()); + } + + @Test + void testOnFileDeleted() throws IOException { + File file = createRandomTempFile(); + FileChangedEvent event = new FileChangedEvent(file, DELETED); + listener.onFileDeleted(event); + assertSame(file, event.getFile()); + assertSame(DELETED, event.getKind()); + } +} \ No newline at end of file From c317fc1ecd686c639de337adec8f17d076229b46 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 19:10:14 +0800 Subject: [PATCH 163/438] Use assertNotNull for kind validation Replace the manual null check in FileChangedEvent constructor with a call to io.microsphere.util.Assert.assertNotNull (imported statically). This simplifies the code and centralizes null validation while preserving the original error message when 'kind' is null. --- .../main/java/io/microsphere/io/event/FileChangedEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/event/FileChangedEvent.java b/microsphere-java-core/src/main/java/io/microsphere/io/event/FileChangedEvent.java index 086cdfe83..847efc57c 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/event/FileChangedEvent.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/event/FileChangedEvent.java @@ -21,6 +21,8 @@ import java.io.File; +import static io.microsphere.util.Assert.assertNotNull; + /** * The event raised when the {@link File file} is changed * @@ -42,9 +44,7 @@ public class FileChangedEvent extends Event { */ public FileChangedEvent(File file, Kind kind) throws IllegalArgumentException { super(file); - if (kind == null) { - throw new IllegalArgumentException("The 'kind' argument must not be null"); - } + assertNotNull(kind, () -> "The 'kind' argument must not be null"); this.kind = kind; } From 44b2b3c0bbc41ff29cf83b50b2e61b64ebfe2aea Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 19:17:22 +0800 Subject: [PATCH 164/438] Use nested Kind and default case for DELETED Import FileChangedEvent.Kind and use the shortened Kind type instead of the fully-qualified reference. Simplify the switch by treating DELETED as the default branch, and update Javadoc @link references to use Kind. This reduces verbosity and slightly simplifies event handling in FileChangedListener. --- .../io/microsphere/io/event/FileChangedListener.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/event/FileChangedListener.java b/microsphere-java-core/src/main/java/io/microsphere/io/event/FileChangedListener.java index 53ece6b00..26bc767ed 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/event/FileChangedListener.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/event/FileChangedListener.java @@ -18,6 +18,7 @@ import io.microsphere.event.Event; import io.microsphere.event.EventListener; +import io.microsphere.io.event.FileChangedEvent.Kind; /** * A listener interface for receiving file change events. @@ -94,7 +95,7 @@ public interface FileChangedListener extends EventListener { default void onEvent(FileChangedEvent event) { - FileChangedEvent.Kind kind = event.getKind(); + Kind kind = event.getKind(); switch (kind) { case CREATED: onFileCreated(event); @@ -102,7 +103,7 @@ default void onEvent(FileChangedEvent event) { case MODIFIED: onFileModified(event); break; - case DELETED: + default: onFileDeleted(event); break; } @@ -111,7 +112,7 @@ default void onEvent(FileChangedEvent event) { /** * Invoked when the file has been created * - * @param event the {@link FileChangedEvent.Kind#CREATED created} {@link FileChangedEvent event} + * @param event the {@link Kind#CREATED created} {@link FileChangedEvent event} */ default void onFileCreated(FileChangedEvent event) { } @@ -119,7 +120,7 @@ default void onFileCreated(FileChangedEvent event) { /** * Invoked when the file has been modified * - * @param event the {@link FileChangedEvent.Kind#MODIFIED modified} {@link FileChangedEvent event} + * @param event the {@link Kind#MODIFIED modified} {@link FileChangedEvent event} */ default void onFileModified(FileChangedEvent event) { } @@ -127,7 +128,7 @@ default void onFileModified(FileChangedEvent event) { /** * Invoked when the file has been deleted * - * @param event the {@link FileChangedEvent.Kind#DELETED deleted} {@link FileChangedEvent event} + * @param event the {@link Kind#DELETED deleted} {@link FileChangedEvent event} */ default void onFileDeleted(FileChangedEvent event) { } From d6af477f1ca0ec2cca74e064aa00571ce000e4ed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 19:23:55 +0800 Subject: [PATCH 165/438] Add test for findAttributeValue with null element Add a unit test that verifies AnnotationUtils.findAttributeValue returns null when the annotations array contains a null (uninitialized) element. Also add a static import of System.arraycopy used to build the test array. This ensures the method handles arrays with null entries without throwing. --- .../java/io/microsphere/util/AnnotationUtilsTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java index 20b42c503..251385525 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java @@ -64,6 +64,7 @@ import static io.microsphere.util.AnnotationUtils.isType; import static io.microsphere.util.ArrayUtils.ofArray; import static io.microsphere.util.ClassLoaderUtils.isPresent; +import static java.lang.System.arraycopy; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; @@ -424,6 +425,14 @@ void testFindAttributeValueOnAttributeNotFound() { assertNull(findAttributeValue(annotationsOfA, "value")); } + @Test + void testFindAttributeValueOnNullAnnnotationElement() { + int length = annotationsOfA.length; + Annotation[] annotations = new Annotation[length + 1]; + arraycopy(annotationsOfA, 0, annotations, 0, length); + assertNull(findAttributeValue(annotations, "value")); + } + @Test void testGetAttributeValue() { ElementType[] elementTypes = getAttributeValue(DataAccess.class.getAnnotation(Target.class), "value"); From 9fe9bd17dfb057a47b7203f5bd4abeb304e95b0f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 19:53:46 +0800 Subject: [PATCH 166/438] Add negative tests for AnnotationUtils Add unit tests to AnnotationUtilsTest covering negative cases: verify exists() returns false for empty iterables and when the annotation is not present, and verify isAnnotationPresent() returns false when given an empty collection of annotation types or when the requested annotation type is not found. These tests strengthen coverage for edge cases and null/empty inputs. --- .../io/microsphere/util/AnnotationUtils.java | 3 +- .../microsphere/util/AnnotationUtilsTest.java | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/AnnotationUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/AnnotationUtils.java index 15bbe7b1f..774358656 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/AnnotationUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/AnnotationUtils.java @@ -58,6 +58,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableList; +import static java.util.Objects.nonNull; /** * {@link Annotation} Utilities class @@ -1652,7 +1653,7 @@ public static boolean isAnnotationInterfaceMethod(Method attributeMethod) { * @return {@code true} if the {@link jdk.internal.reflect.CallerSensitive} annotation is present; {@code false} otherwise */ public static boolean isCallerSensitivePresent() { - return CALLER_SENSITIVE_ANNOTATION_CLASS != null; + return nonNull(CALLER_SENSITIVE_ANNOTATION_CLASS); } private AnnotationUtils() { diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java index 251385525..d38b0f1a8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java @@ -248,6 +248,7 @@ void testIsMetaAnnotationWithAnnotationTypeAndMetadataAnnotationTypes() { assertTrue(isMetaAnnotation(DataAccess.class, ServiceMode.class, Target.class)); assertTrue(isMetaAnnotation(DataAccess.class, Target.class, ServiceMode.class)); assertTrue(isMetaAnnotation(DataAccess.class, ofArray(ServiceMode.class))); + assertFalse(isMetaAnnotation(DataAccess.class, ofArray(Since.class))); } @Test @@ -273,6 +274,13 @@ void testIsMetaAnnotationWithAnnotationTypeAndIterableOfMetadataAnnotationTypesO assertFalse(isMetaAnnotation(DataAccess.class, TEST_EMPTY_LIST)); } + @Test + void testIsMetaAnnotationWithAnnotationTypeAndIterableOfMetadataAnnotationTypesOnNativeAnnotations() { + NATIVE_ANNOTATION_TYPES.forEach(a -> { + assertFalse(isMetaAnnotation(a, TEST_EMPTY_LIST)); + }); + } + @Test void testGetAllDeclaredAnnotations() { List annotations = getAllDeclaredAnnotations(A.class); @@ -508,6 +516,17 @@ void testExistsOnNull() { assertFalse(exists(ofList(annotationsOfA), null)); } + @Test + void testExistsOnEmptyIterable() { + assertFalse(exists(emptyList(), DataAccess.class)); + assertFalse(exists((Iterable) emptyList(), DataAccess.class)); + } + + @Test + void testExistsOnNotFound() { + assertFalse(exists(annotationsOfA, Override.class)); + } + @Test void testIsAnnotatedPresentWithArray() { assertTrue(isAnnotationPresent(ofArray(A.class), DataAccess.class)); @@ -577,6 +596,16 @@ void testIsAnnotatedPresentWithAnnotatedElementAndAnnotationTypesOnNull() { assertFalse(isAnnotationPresent(B.class, TEST_NULL_ITERABLE)); } + @Test + void testIsAnnotatedPresentWithAnnotatedElementAndAnnotationTypesOnEmpty() { + assertFalse(isAnnotationPresent(B.class, emptyList())); + } + + @Test + void testIsAnnotatedPresentWithAnnotatedElementAndAnnotationTypesOnNotFound() { + assertFalse(isAnnotationPresent(B.class, ofList(Override.class))); + } + @Test void testIsAnnotationInterfaceMethod() { assertTrue(isAnnotationInterfaceMethod(annotationTypeMethod)); From 65614d0a1f911f838acfc2d537da7173a7083c83 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 20:05:39 +0800 Subject: [PATCH 167/438] Refactor FileExtensionFilter and extend tests Introduce a protected matches(fileExt, caseSensitive) method and refactor FileExtensionFilter to use it instead of inline OS checks (case sensitivity uses !IS_OS_WINDOWS). Update tests to reference this.instance, add a test for files without extensions, and add explicit tests covering case-sensitive and case-insensitive matching behavior. --- .../io/filter/FileExtensionFilter.java | 9 +++++-- .../io/filter/FileExtensionFilterTest.java | 24 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/filter/FileExtensionFilter.java b/microsphere-java-core/src/main/java/io/microsphere/io/filter/FileExtensionFilter.java index 71d885d8c..1436c4bbe 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/filter/FileExtensionFilter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/filter/FileExtensionFilter.java @@ -75,7 +75,11 @@ public boolean accept(File file) { return false; } - return IS_OS_WINDOWS ? fileExtension.equalsIgnoreCase(extension) : fileExtension.equals(extension); + return matches(fileExtension, !IS_OS_WINDOWS); + } + + protected boolean matches(String fileExtension, boolean caseSensitive) { + return caseSensitive ? fileExtension.equals(extension) : fileExtension.equalsIgnoreCase(extension); } /** @@ -87,4 +91,5 @@ public boolean accept(File file) { public static FileExtensionFilter of(String extension) { return new FileExtensionFilter(extension); } -} + +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/filter/FileExtensionFilterTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/filter/FileExtensionFilterTest.java index c5115b957..5a3b1f671 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/filter/FileExtensionFilterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/filter/FileExtensionFilterTest.java @@ -27,25 +27,37 @@ class FileExtensionFilterTest { @BeforeEach void setUp() { - instance = of("txt"); + this.instance = of("txt"); } @Test void testAcceptOnNull() { - assertFalse(instance.accept(null)); + assertFalse(this.instance.accept(null)); } @Test void testAcceptOnDirectory() { - assertFalse(instance.accept(new File(JAVA_HOME))); - assertFalse(instance.accept(new File(USER_DIR))); - assertFalse(instance.accept(new File(JAVA_IO_TMPDIR))); + assertFalse(this.instance.accept(new File(JAVA_HOME))); + assertFalse(this.instance.accept(new File(USER_DIR))); + assertFalse(this.instance.accept(new File(JAVA_IO_TMPDIR))); } @Test void testAcceptOnFile() throws IOException { File testFile = createTempFile("test", ".txt"); - assertTrue(instance.accept(testFile)); + assertTrue(this.instance.accept(testFile)); testFile.deleteOnExit(); + + testFile = createTempFile("test", ""); + assertFalse(this.instance.accept(testFile)); + testFile.deleteOnExit(); + } + + @Test + void testMatches() { + assertTrue(this.instance.matches("txt", true)); + assertFalse(this.instance.matches("TXT", true)); + assertTrue(this.instance.matches("txt", false)); + assertTrue(this.instance.matches("TXT", false)); } } \ No newline at end of file From 595dc78c75bdc64862320a89628038efa206556b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 20:23:06 +0800 Subject: [PATCH 168/438] Refactor Prioritized comparator logic Rework the COMPARATOR in Prioritized to simplify branching and improve readability. The new structure checks if the first operand is Prioritized, then handles the both-Prioritized case by calling compareTo, otherwise returns -1; if only the second is Prioritized it returns 1; otherwise returns 0. Added clearer inline comments and moved the final return to the end. No functional behavior intended to change. --- .../java/io/microsphere/lang/Prioritized.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/lang/Prioritized.java b/microsphere-java-core/src/main/java/io/microsphere/lang/Prioritized.java index b8a94072c..b369e3840 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/lang/Prioritized.java +++ b/microsphere-java-core/src/main/java/io/microsphere/lang/Prioritized.java @@ -61,15 +61,18 @@ public interface Prioritized extends Comparable { Comparator COMPARATOR = (one, two) -> { boolean b1 = one instanceof Prioritized; boolean b2 = two instanceof Prioritized; - if (b1 && !b2) { // one is Prioritized, two is not - return -1; - } else if (b2 && !b1) { // two is Prioritized, one is not + + if (b1) { // one is Prioritized + if (b2) { // two is also Prioritized + return ((Prioritized) one).compareTo((Prioritized) two); + } else { // two is not Prioritized yet + return -1; + } + } else if (b2) { // one is not Prioritized, and two is Prioritized return 1; - } else if (b1 && b2) { // one and two both are Prioritized - return ((Prioritized) one).compareTo((Prioritized) two); - } else { // no different - return 0; } + + return 0; // no different }; /** From ff0b2adf12df944e75695616139db95e5ec84464 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 20:23:13 +0800 Subject: [PATCH 169/438] Add comparator assertions in PrioritizedTest Expand PrioritizedTest with additional assertions to verify Comparator behavior for null and String operands. The changes add cases comparing null with a prioritized instance and with the literal "prioritized", as well as symmetric and identical string comparisons, to ensure consistent compare(...) results across these scenarios. --- .../src/test/java/io/microsphere/lang/PrioritizedTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/PrioritizedTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/PrioritizedTest.java index 982e75c38..8287b3a21 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/PrioritizedTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/PrioritizedTest.java @@ -66,6 +66,10 @@ void testComparator() { assertEquals(1, COMPARATOR.compare(null, prioritized)); assertEquals(-1, COMPARATOR.compare(prioritized, null)); assertEquals(0, COMPARATOR.compare(null, null)); + assertEquals(1, COMPARATOR.compare(null, prioritized)); + assertEquals(0, COMPARATOR.compare(null, "prioritized")); + assertEquals(0, COMPARATOR.compare("prioritized", "prioritized")); + assertEquals(0, COMPARATOR.compare("prioritized", null)); } } \ No newline at end of file From b2e6d2df5c2d13733571b60a0841dde316945b27 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 18 Feb 2026 23:40:43 +0800 Subject: [PATCH 170/438] Refactor StackTraceUtils StackWalker init Move StackWalker-related class/method resolution to static field initializers and simplify static initialization. Remove the testing-only stackWalkerSupportedForTesting flag and make stackWalkerInstance nullable; initialize it via invokeStaticMethod when available. Expose new internal APIs getCallerClassName(Object,int) and getCallerClassNames(Object) to allow using a specific StackWalker instance and a frame offset, and adjust caller-index calculations accordingly. Update tests to call the new API signature. --- .../io/microsphere/util/StackTraceUtils.java | 61 ++++++------------- .../util/StackTraceElementUtilsBenchmark.java | 4 +- .../microsphere/util/StackTraceUtilsTest.java | 4 +- 3 files changed, 22 insertions(+), 47 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java index b148c2104..f120b9d96 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java @@ -69,19 +69,19 @@ public abstract class StackTraceUtils implements Utils { * The {@link Class} of {@linkplain java.lang.StackWalker} that was introduced in JDK 9. * (optional) */ - public static final @Nullable Class STACK_WALKER_CLASS; + public static final @Nullable Class STACK_WALKER_CLASS = resolveClass(STACK_WALKER_CLASS_NAME); /** * The {@link Class} of {@linkplain java.lang.StackWalker.Option} that was introduced in JDK 9. * (optional) */ - public static final @Nullable Class STACK_WALKER_OPTION_CLASS; + public static final @Nullable Class STACK_WALKER_OPTION_CLASS = resolveClass(STACK_WALKER_OPTION_CLASS_NAME); /** * The {@link Class} of {@linkplain java.lang.StackWalker.StackFrame} that was introduced in JDK 9. * (optional) */ - public static final @Nullable Class STACK_WALKER_STACK_FRAME_CLASS; + public static final @Nullable Class STACK_WALKER_STACK_FRAME_CLASS = resolveClass(STACK_WALKER_STACK_FRAME_CLASS_NAME); /** * The name of {@linkplain java.lang.StackWalker.Option#RETAIN_CLASS_REFERENCE} @@ -113,16 +113,11 @@ public abstract class StackTraceUtils implements Utils { */ static final String GET_CLASS_NAME_METHOD_NAME = "getClassName"; - static final Method WALK_METHOD; + static final Method WALK_METHOD = findMethod(STACK_WALKER_CLASS, WALK_METHOD_NAME, Function.class); - static final Method GET_CLASS_NAME_METHOD; + static final Method GET_CLASS_NAME_METHOD = findMethod(STACK_WALKER_STACK_FRAME_CLASS, GET_CLASS_NAME_METHOD_NAME); - private static final Object stackWalkerInstance; - - /** - * The testing propose to test whether {@linkplain java.lang.StackWalker} is supported or not. - */ - static boolean stackWalkerSupportedForTesting; + private static @Nullable Object stackWalkerInstance; private static final Function, Object> getClassNamesFunction = StackTraceUtils::getCallerClassNames; @@ -138,35 +133,9 @@ public abstract class StackTraceUtils implements Utils { // Initialize java.lang.StackWalker static { - Class stackWalkerClass = null; - Class stackWalkerOptionClass = null; - Class stackWalkerStackFrameClass = null; - Method walkMethod = null; - Method getClassNameMethod = null; - Object stackWalker = null; - - if (IS_JDK_9_OR_LATER) { - stackWalkerClass = resolveClass(STACK_WALKER_CLASS_NAME); - stackWalkerOptionClass = resolveClass(STACK_WALKER_OPTION_CLASS_NAME); - stackWalkerStackFrameClass = resolveClass(STACK_WALKER_STACK_FRAME_CLASS_NAME); - walkMethod = findMethod(stackWalkerClass, WALK_METHOD_NAME, Function.class); - walkMethod.setAccessible(true); - getClassNameMethod = findMethod(stackWalkerStackFrameClass, GET_CLASS_NAME_METHOD_NAME); - getClassNameMethod.setAccessible(true); - stackWalker = invokeStaticMethod(stackWalkerClass, GET_INSTANCE_METHOD_NAME); - stackWalkerSupportedForTesting = true; - } - - STACK_WALKER_CLASS = stackWalkerClass; - STACK_WALKER_OPTION_CLASS = stackWalkerOptionClass; - STACK_WALKER_STACK_FRAME_CLASS = stackWalkerStackFrameClass; - WALK_METHOD = walkMethod; - GET_CLASS_NAME_METHOD = getClassNameMethod; - stackWalkerInstance = stackWalker; - int invocationFrame = 0; - if (IS_JDK_9_OR_LATER) { + stackWalkerInstance = invokeStaticMethod(STACK_WALKER_CLASS, GET_INSTANCE_METHOD_NAME); List stackFrameClassNames = getCallerClassNames(); for (String stackFrameClassName : stackFrameClassNames) { if (TYPE.getName().equals(stackFrameClassName)) { @@ -235,17 +204,25 @@ public static StackTraceElement[] getStackTrace() { * @throws IndexOutOfBoundsException if the stack trace does not have enough frames to determine the caller */ public static String getCallerClassName() { - if (stackWalkerInstance == null || !stackWalkerSupportedForTesting) { + return getCallerClassName(stackWalkerInstance, 1); + } + + static String getCallerClassName(Object stackWalkerInstance, int frameOffSet) { + if (stackWalkerInstance == null) { // Plugs 1 , because Invocation getStackTrace() method was considered as increment invocation frame // Plugs 1 , because Invocation getCallerClassName() method was considered as increment invocation frame // Plugs 1 , because Invocation getCallerClassNameInGeneralJVM(int) method was considered as increment invocation frame - return getCallerClassNameInGeneralJVM(stackTraceElementInvocationFrame + 3); + return getCallerClassNameInGeneralJVM(stackTraceElementInvocationFrame + 3 + frameOffSet); } - List callerClassNames = getCallerClassNames(); - String className = callerClassNames.get(stackWalkerInvocationFrame); + List callerClassNames = getCallerClassNames(stackWalkerInstance); + String className = callerClassNames.get(stackWalkerInvocationFrame + frameOffSet); return className; } + static List getCallerClassNames(Object stackWalkerInstance) { + return invokeMethod(stackWalkerInstance, WALK_METHOD, getClassNamesFunction); + } + static List getCallerClassNames() { return invokeMethod(stackWalkerInstance, WALK_METHOD, getClassNamesFunction); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceElementUtilsBenchmark.java b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceElementUtilsBenchmark.java index a705449b8..306ec684c 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceElementUtilsBenchmark.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceElementUtilsBenchmark.java @@ -25,8 +25,8 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import static io.microsphere.util.StackTraceUtils.getCallerClassName; import static io.microsphere.util.StackTraceUtils.getCallerClassNameInGeneralJVM; -import static io.microsphere.util.StackTraceUtils.getCallerClassNames; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.openjdk.jmh.annotations.Mode.AverageTime; @@ -53,6 +53,6 @@ public void testGetCallerClassNameInGeneralJVM() { @Benchmark public void testGetCallerClassName() { - getCallerClassNames(); + getCallerClassName(); } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java index 189203808..d774da899 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java @@ -31,10 +31,8 @@ void testGetCallerClassName() { @Test void testGetCallerClassNameOnStackWalkerSupportedForTesting() { - StackTraceUtils.stackWalkerSupportedForTesting = false; - assertEquals(getCallerClassNameInGeneralJVM(), getCallerClassName()); + assertEquals(getCallerClassNameInGeneralJVM(), getCallerClassName(null, 0)); assertEquals(CALLER_CLASS_NAME, getCallerClassName()); - StackTraceUtils.stackWalkerSupportedForTesting = true; } @Test From 14299d7b8984d302992b283441ffe15a96c26d52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 01:33:50 +0000 Subject: [PATCH 171/438] Bump org.springframework:spring-framework-bom from 7.0.3 to 7.0.5 Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 7.0.3 to 7.0.5. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v7.0.3...v7.0.5) --- updated-dependencies: - dependency-name: org.springframework:spring-framework-bom dependency-version: 7.0.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-java-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 23460ca6c..f8e25ed88 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -22,7 +22,7 @@ 1.3.2 3.0.2 1.3.2 - 7.0.3 + 7.0.5 2.0.17 1.5.24 From 202f3244ebe895dee9fc28075d151630ad3a506a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:09:55 +0800 Subject: [PATCH 172/438] Use ArrayUtils and static imports across project Refactor to prefer shared utility methods and concise static imports for consistency and readability. Replaced direct Arrays.equals usages with io.microsphere.util.ArrayUtils.arrayEquals (multiple sources/tests), switched to java.lang.reflect.Array static methods (get, getLength, set) to avoid qualified Array calls, and imported various static helpers (Stream.of, Optional.of/empty, Thread.sleep, String.format -> format, Spliterator.ORDERED, Version.of, enum values()). Updated affected tests and utility classes to use the new imports and helpers. --- .../io/microsphere/collection/ArrayEnumeration.java | 4 ++-- .../java/io/microsphere/collection/MapUtils.java | 3 ++- .../io/microsphere/collection/ReversedDeque.java | 3 ++- .../convert/multiple/StringToArrayConverter.java | 5 ++--- .../io/microsphere/io/FastByteArrayInputStream.java | 4 ++-- .../main/java/io/microsphere/json/JSONStringer.java | 3 ++- .../java/io/microsphere/lang/function/Streams.java | 3 ++- .../microsphere/reflect/ExecutableDefinition.java | 3 ++- .../io/microsphere/reflect/MethodDefinition.java | 3 ++- .../io/microsphere/reflect/ReflectionUtils.java | 6 ++++-- .../convert/ObjectToOptionalConverterTest.java | 6 ++++-- .../io/microsphere/io/FileWatchServiceTest.java | 6 +++--- .../src/test/java/io/microsphere/test/Data.java | 10 +++++++--- .../java/io/microsphere/test/MultipleValueData.java | 4 +++- .../java/io/microsphere/util/StopWatchTest.java | 5 +++-- .../java/io/microsphere/util/SystemUtilsTest.java | 4 ++-- .../java/io/microsphere/util/TypeFinderTest.java | 13 +++++++------ .../java/io/microsphere/test/model/ColorTest.java | 3 ++- .../microsphere/lang/model/util/FieldUtilsTest.java | 3 ++- 19 files changed, 55 insertions(+), 36 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/ArrayEnumeration.java b/microsphere-java-core/src/main/java/io/microsphere/collection/ArrayEnumeration.java index 2a58a8615..151487742 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/collection/ArrayEnumeration.java +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/ArrayEnumeration.java @@ -16,7 +16,6 @@ */ package io.microsphere.collection; -import java.util.Arrays; import java.util.Enumeration; import java.util.NoSuchElementException; import java.util.StringJoiner; @@ -25,6 +24,7 @@ import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET; import static io.microsphere.constants.SymbolConstants.RIGHT_SQUARE_BRACKET; import static io.microsphere.constants.SymbolConstants.SPACE; +import static io.microsphere.util.ArrayUtils.arrayEquals; import static java.lang.String.valueOf; import static java.util.Objects.hash; @@ -85,7 +85,7 @@ public final boolean equals(Object o) { if (!(o instanceof ArrayEnumeration)) return false; ArrayEnumeration that = (ArrayEnumeration) o; - return Arrays.equals(elements, that.elements); + return arrayEquals(elements, that.elements); } @Override diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/MapUtils.java b/microsphere-java-core/src/main/java/io/microsphere/collection/MapUtils.java index afc9fba4e..832f4f683 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/collection/MapUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/MapUtils.java @@ -40,6 +40,7 @@ import static io.microsphere.util.ArrayUtils.length; import static io.microsphere.util.ClassUtils.isAssignableFrom; import static java.lang.Float.MIN_VALUE; +import static java.lang.String.valueOf; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableMap; @@ -1184,7 +1185,7 @@ public static Map nestedMap(Map map) { for (Map.Entry entry : map.entrySet()) { String propertyName = entry.getKey(); - String propertyValue = String.valueOf(entry.getValue()); + String propertyValue = valueOf(entry.getValue()); int index = propertyName.indexOf(DOT_CHAR); if (index > 0) { String actualPropertyName = propertyName.substring(0, index); diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/ReversedDeque.java b/microsphere-java-core/src/main/java/io/microsphere/collection/ReversedDeque.java index e55d9ba04..84113b5e0 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/collection/ReversedDeque.java +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/ReversedDeque.java @@ -27,6 +27,7 @@ import static io.microsphere.util.ArrayUtils.reverse; import static io.microsphere.util.ArrayUtils.toArrayReversed; +import static java.util.Spliterator.ORDERED; /** * Reverse ordered {@link Deque} based on JDK 21 {@link java.util.ReverseOrderDequeView} @@ -64,7 +65,7 @@ public Iterator iterator() { @Override public Spliterator spliterator() { - return Spliterators.spliterator(this, Spliterator.ORDERED); + return Spliterators.spliterator(this, ORDERED); } // ========== Collection ========== diff --git a/microsphere-java-core/src/main/java/io/microsphere/convert/multiple/StringToArrayConverter.java b/microsphere-java-core/src/main/java/io/microsphere/convert/multiple/StringToArrayConverter.java index d3a7ad6a0..57fc84822 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/convert/multiple/StringToArrayConverter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/convert/multiple/StringToArrayConverter.java @@ -19,9 +19,8 @@ import io.microsphere.convert.Converter; -import java.lang.reflect.Array; - import static java.lang.reflect.Array.newInstance; +import static java.lang.reflect.Array.set; /** * The class to convert {@link String} to array-type object @@ -44,7 +43,7 @@ public Object convert(String[] segments, int size, Class targetType, Class Object array = newInstance(componentType, size); for (int i = 0; i < size; i++) { - Array.set(array, i, converter.convert(segments[i])); + set(array, i, converter.convert(segments[i])); } return array; diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayInputStream.java b/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayInputStream.java index 46a799ce2..73474abc1 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayInputStream.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayInputStream.java @@ -17,8 +17,8 @@ package io.microsphere.io; import java.io.ByteArrayInputStream; -import java.util.Arrays; +import static io.microsphere.util.ArrayUtils.arrayEquals; import static java.lang.System.arraycopy; import static java.util.Objects.hash; @@ -159,7 +159,7 @@ public boolean equals(Object obj) { if (!(obj instanceof FastByteArrayInputStream)) { return false; } - return Arrays.equals(this.buf, ((FastByteArrayInputStream) obj).buf); + return arrayEquals(this.buf, ((FastByteArrayInputStream) obj).buf); } @Override diff --git a/microsphere-java-core/src/main/java/io/microsphere/json/JSONStringer.java b/microsphere-java-core/src/main/java/io/microsphere/json/JSONStringer.java index 37c6dbd51..ffb062aec 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/json/JSONStringer.java +++ b/microsphere-java-core/src/main/java/io/microsphere/json/JSONStringer.java @@ -27,6 +27,7 @@ import static io.microsphere.json.JSONStringer.Scope.NONEMPTY_ARRAY; import static io.microsphere.json.JSONStringer.Scope.NONEMPTY_OBJECT; import static io.microsphere.util.ClassUtils.getTypeName; +import static java.lang.String.format; import static java.util.Arrays.fill; /** @@ -373,7 +374,7 @@ void string(String value) { default: if (c <= 0x1F) { - this.out.append(String.format("\\u%04x", (int) c)); + this.out.append(format("\\u%04x", (int) c)); } else { this.out.append(c); } diff --git a/microsphere-java-core/src/main/java/io/microsphere/lang/function/Streams.java b/microsphere-java-core/src/main/java/io/microsphere/lang/function/Streams.java index fd367ed55..29273a1ce 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/lang/function/Streams.java +++ b/microsphere-java-core/src/main/java/io/microsphere/lang/function/Streams.java @@ -33,6 +33,7 @@ import static io.microsphere.lang.function.Predicates.or; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; +import static java.util.stream.Stream.of; /** * The utilities class for {@link Stream} @@ -62,7 +63,7 @@ public interface Streams { */ @Nonnull static Stream stream(T... values) { - return Stream.of(values); + return of(values); } /** diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableDefinition.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableDefinition.java index ce1aeab4b..d9a159f2d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableDefinition.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ExecutableDefinition.java @@ -30,6 +30,7 @@ import static io.microsphere.util.Assert.assertNotNull; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; import static io.microsphere.util.ClassLoaderUtils.resolveClass; +import static io.microsphere.util.Version.of; import static java.util.Objects.hash; /** @@ -89,7 +90,7 @@ protected ExecutableDefinition(@Nonnull String since, @Nonnull String declaredCl */ protected ExecutableDefinition(@Nonnull String since, @Nonnull Deprecation deprecation, @Nonnull String declaredClassName, @Nonnull String name, @Nonnull String... parameterClassNames) { - this(Version.of(since), deprecation, declaredClassName, name, parameterClassNames); + this(of(since), deprecation, declaredClassName, name, parameterClassNames); } /** diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodDefinition.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodDefinition.java index 3b0e5d555..74265f946 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodDefinition.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodDefinition.java @@ -28,6 +28,7 @@ import static io.microsphere.reflect.MethodUtils.findMethod; import static io.microsphere.reflect.MethodUtils.invokeMethod; import static io.microsphere.util.ArrayUtils.arrayToString; +import static io.microsphere.util.Version.of; /** * The definition class of Java {@link Method}, providing a structured way to define and resolve methods @@ -74,7 +75,7 @@ public MethodDefinition(String since, String declaredClassName, String methodNam * @param parameterClassNames the parameter class names */ public MethodDefinition(String since, Deprecation deprecation, String declaredClassName, String methodName, String... parameterClassNames) { - this(Version.of(since), deprecation, declaredClassName, methodName, parameterClassNames); + this(of(since), deprecation, declaredClassName, methodName, parameterClassNames); } /** diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java index c675db165..f5bf4c3ca 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java @@ -26,6 +26,8 @@ import static io.microsphere.util.ClassUtils.isSimpleType; import static java.lang.Class.forName; import static java.lang.Thread.currentThread; +import static java.lang.reflect.Array.get; +import static java.lang.reflect.Array.getLength; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; @@ -381,10 +383,10 @@ static Class getCallerClassInGeneralJVM() { */ @Nonnull public static List toList(Object array) throws IllegalArgumentException { - int length = Array.getLength(array); + int length = getLength(array); List list = new ArrayList<>(length); for (int i = 0; i < length; i++) { - Object element = Array.get(array, i); + Object element = get(array, i); list.add((T) toObject(element)); } return list; diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/ObjectToOptionalConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/ObjectToOptionalConverterTest.java index 363d88645..78d4df1d3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/ObjectToOptionalConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/ObjectToOptionalConverterTest.java @@ -21,6 +21,8 @@ import java.util.Optional; +import static java.util.Optional.empty; +import static java.util.Optional.of; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -45,7 +47,7 @@ void testAccept() { @Test void testConvert() { - assertEquals(Optional.of("1"), converter.convert("1")); - assertEquals(Optional.empty(), converter.convert(null)); + assertEquals(of("1"), converter.convert("1")); + assertEquals(empty(), converter.convert(null)); } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java index 71852d986..1e1e2aea2 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java @@ -1,13 +1,13 @@ package io.microsphere.io; import io.microsphere.AbstractTestCase; -import io.microsphere.io.event.FileChangedEvent; import io.microsphere.io.event.LoggingFileChangedListener; import org.junit.jupiter.api.Test; import java.io.File; import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.io.event.FileChangedEvent.Kind.values; /** * {@link FileWatchService} Test @@ -25,7 +25,7 @@ class FileWatchServiceTest extends AbstractTestCase { @Test void testWatch() { File file = newRandomTempFile(); - service.watch(file, new LoggingFileChangedListener(), FileChangedEvent.Kind.values()); - service.watch(file, ofList(new LoggingFileChangedListener()), FileChangedEvent.Kind.values()); + service.watch(file, new LoggingFileChangedListener(), values()); + service.watch(file, ofList(new LoggingFileChangedListener()), values()); } } \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/test/Data.java b/microsphere-java-core/src/test/java/io/microsphere/test/Data.java index 5c06f8973..fedf7b861 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/test/Data.java +++ b/microsphere-java-core/src/test/java/io/microsphere/test/Data.java @@ -20,6 +20,10 @@ import java.util.Arrays; import java.util.Objects; +import static io.microsphere.util.ArrayUtils.arrayEquals; +import static java.lang.Double.compare; +import static java.lang.Float.compare; + /** * Data Model * @@ -162,15 +166,15 @@ public final boolean equals(Object o) { Data data = (Data) o; return age == data.age && male == data.male - && Double.compare(height, data.height) == 0 - && Float.compare(weight, data.weight) == 0 + && compare(height, data.height) == 0 + && compare(weight, data.weight) == 0 && birth == data.birth && index == data.index && grade == data.grade && sex == data.sex && Objects.equals(this.name, data.name) && Objects.equals(this.object, data.object) - && Arrays.equals(names, data.names); + && arrayEquals(names, data.names); } @Override diff --git a/microsphere-java-core/src/test/java/io/microsphere/test/MultipleValueData.java b/microsphere-java-core/src/test/java/io/microsphere/test/MultipleValueData.java index c06285949..4053db692 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/test/MultipleValueData.java +++ b/microsphere-java-core/src/test/java/io/microsphere/test/MultipleValueData.java @@ -26,6 +26,8 @@ import java.util.Queue; import java.util.Set; +import static io.microsphere.util.ArrayUtils.arrayEquals; + /** * Multi-Value Data * @@ -107,7 +109,7 @@ public final boolean equals(Object o) { && Objects.equals(integerSet, that.integerSet) && CollectionUtils.equals(dataQueue, that.dataQueue) && Objects.equals(classEnumeration, that.classEnumeration) - && Arrays.equals(objects, that.objects); + && arrayEquals(objects, that.objects); } @Override diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java index 2e0ec9f22..b5504aa06 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java @@ -24,6 +24,7 @@ import static io.microsphere.constants.SymbolConstants.SPACE; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.StopWatch.Task.start; +import static java.lang.Thread.sleep; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -57,9 +58,9 @@ void setUp() { @Test void test() throws InterruptedException { stopWatch.start("1"); - Thread.sleep(100); + sleep(100); stopWatch.start("2"); - Thread.sleep(10); + sleep(10); stopWatch.stop(); stopWatch.stop(); Task currentTask = stopWatch.getCurrentTask(); diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/SystemUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/SystemUtilsTest.java index 4cbacf54c..6285786fd 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/SystemUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/SystemUtilsTest.java @@ -25,7 +25,6 @@ import java.lang.reflect.Field; import java.util.Map; import java.util.Objects; -import java.util.stream.Stream; import static io.microsphere.collection.MapUtils.ofMap; import static io.microsphere.util.StringUtils.substringAfter; @@ -96,6 +95,7 @@ import static io.microsphere.util.SystemUtils.getSystemPropertyFromCopy; import static io.microsphere.util.SystemUtils.resetSystemPropertiesCopy; import static java.lang.System.getProperty; +import static java.util.stream.Stream.of; import static javax.lang.model.SourceVersion.latest; import static javax.lang.model.SourceVersion.values; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -139,7 +139,7 @@ class SystemUtilsTest { private static final SourceVersion[] versions = values(); private static Field[] findIsJavaVersionFields() { - return Stream.of(CLASS.getFields()) + return of(CLASS.getFields()) .filter(MemberUtils::isStatic) .filter(field -> field.getName().startsWith(IS_JAVA_VERSION_FIELD_NAME_PREFIX)) .toArray(Field[]::new); diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java index 88aada7c1..1c5b3b44e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java @@ -19,6 +19,7 @@ import static io.microsphere.util.TypeFinder.Include.HIERARCHICAL; import static io.microsphere.util.TypeFinder.Include.INTERFACES; import static io.microsphere.util.TypeFinder.Include.SUPER_CLASS; +import static io.microsphere.util.TypeFinder.Include.values; import static io.microsphere.util.TypeFinder.classFinder; import static io.microsphere.util.TypeFinder.genericTypeFinder; import static java.util.Collections.emptyList; @@ -40,9 +41,9 @@ class TypeFinderTest extends AbstractTestCase { @Test void testConstructorOnNullType() { assertThrows(IllegalArgumentException.class, () -> classFinder(null, true, true, true, true)); - assertThrows(IllegalArgumentException.class, () -> classFinder(null, Include.values())); + assertThrows(IllegalArgumentException.class, () -> classFinder(null, values())); assertThrows(IllegalArgumentException.class, () -> classFinder(null, true, true, true, true)); - assertThrows(IllegalArgumentException.class, () -> genericTypeFinder(null, Include.values())); + assertThrows(IllegalArgumentException.class, () -> genericTypeFinder(null, values())); assertThrows(IllegalArgumentException.class, () -> genericTypeFinder(null, true, true, true, true)); } @@ -87,7 +88,7 @@ void testGetAllInheritedClasses() { @Test void testGetAllClasses() { - TypeFinder> classFinder = classFinder(StringIntegerToBooleanClass.class, Include.values()); + TypeFinder> classFinder = classFinder(StringIntegerToBooleanClass.class, values()); List> types = classFinder.getTypes(); assertValues(types, StringIntegerToBooleanClass.class, Object.class, StringIntegerToBoolean.class, StringIntegerF1.class, StringBF2.class, BF3.class, BiFunction.class); } @@ -115,7 +116,7 @@ void testFindAllInheritedClasses() { @Test void testFindAllClasses() { - TypeFinder> classFinder = classFinder(StringIntegerToBooleanClass.class, Include.values()); + TypeFinder> classFinder = classFinder(StringIntegerToBooleanClass.class, values()); List> types = classFinder.findTypes(NON_OBJECT_TYPE_FILTER); assertValues(types, StringIntegerToBooleanClass.class, StringIntegerToBoolean.class, StringIntegerF1.class, StringBF2.class, BF3.class, BiFunction.class); } @@ -146,7 +147,7 @@ void testGetAllGenericInheritedClasses() { @Test void testGetAllGenericClasses() { - TypeFinder genericTypeFinder = genericTypeFinder(StringIntegerToBooleanClass.class, Include.values()); + TypeFinder genericTypeFinder = genericTypeFinder(StringIntegerToBooleanClass.class, values()); List types = genericTypeFinder.getTypes(); assertEquals(7, types.size()); assertEquals(StringIntegerToBooleanClass.class, types.get(0)); @@ -179,7 +180,7 @@ void testFindAllGenericInheritedClasses() { @Test void testFindAllGenericClasses() { - TypeFinder genericTypeFinder = genericTypeFinder(StringIntegerToBooleanClass.class, Include.values()); + TypeFinder genericTypeFinder = genericTypeFinder(StringIntegerToBooleanClass.class, values()); List types = genericTypeFinder.findTypes(NON_OBJECT_TYPE_FILTER); assertEquals(6, types.size()); assertEquals(StringIntegerToBooleanClass.class, types.get(0)); diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/ColorTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/ColorTest.java index 9f8f0cfde..cd3f6557e 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/model/ColorTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/ColorTest.java @@ -5,6 +5,7 @@ import static io.microsphere.test.model.Color.BLUE; import static io.microsphere.test.model.Color.RED; import static io.microsphere.test.model.Color.YELLOW; +import static io.microsphere.test.model.Color.values; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -92,7 +93,7 @@ void testValueImmutability() { @Test void testAllEnumConstantsExist() { // Verify that we can retrieve all enum constants - Color[] allColors = Color.values(); + Color[] allColors = values(); assertEquals(3, allColors.length, "There should be exactly 3 color constants"); // Verify that all expected colors are present diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/FieldUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/FieldUtilsTest.java index afa535fb0..4252008b7 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/FieldUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/FieldUtilsTest.java @@ -48,6 +48,7 @@ import static io.microsphere.lang.model.util.FieldUtils.isField; import static io.microsphere.lang.model.util.FieldUtils.isNonStaticField; import static io.microsphere.lang.model.util.MethodUtils.findMethod; +import static io.microsphere.test.model.Color.values; import static io.microsphere.util.StringUtils.EMPTY_STRING; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; @@ -266,7 +267,7 @@ void testIsNonStaticField() { @Test void testIsNonStaticFieldOnStaticField() { TypeElement type = getTypeElement(Color.class); - for (Color color : Color.values()) { + for (Color color : values()) { assertFalse(isNonStaticField(findField(type, color.name()))); } } From 86efb19a00a4583102c91eec4802117e9794627d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:16:29 +0800 Subject: [PATCH 173/438] Refactor Lookup and import usages Replace fully-qualified MethodHandles.Lookup references with imported Lookup type and update related constructors, cache and method signatures. Add static imports for java.util.logging.Level constants and java.util.Calendar utilities; simplify Prioritized.COMPARATOR usages to COMPARATOR. Update tests to use static Boolean TRUE/FALSE, Void.TYPE alias, JavaType.Kind and Modifier.MANDATED imports for clarity. Overall cleanup improves readability and consistency of imports and type references. --- .../microsphere/invoke/MethodHandleUtils.java | 93 ++++++++++--------- .../microsphere/logging/JDKLoggerFactory.java | 36 ++++--- .../io/microsphere/logging/LoggerFactory.java | 2 +- ...positeSubProtocolURLConnectionFactory.java | 3 +- .../net/CompositeURLStreamHandlerFactory.java | 3 +- .../ExtendableProtocolURLStreamHandler.java | 4 +- .../io/microsphere/beans/BeanUtilsTest.java | 8 +- .../convert/StringToBooleanConverterTest.java | 3 +- .../io/microsphere/json/JSONUtilsTest.java | 3 +- .../logging/LoggerFactoryTest.java | 5 +- .../CompositeURLStreamHandlerFactoryTest.java | 4 +- .../io/microsphere/reflect/JavaTypeTest.java | 10 +- .../io/microsphere/reflect/ModifierTest.java | 3 +- .../test/model/SimpleTypeModelTest.java | 8 +- 14 files changed, 102 insertions(+), 83 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java index ac7cb7552..49f07cfea 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandleUtils.java @@ -22,6 +22,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -88,7 +89,7 @@ public abstract class MethodHandleUtils implements Utils { /** * A single-bit mask representing {@code module} access, - * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. + * which may contribute to the result of {@link Lookup#lookupModes lookupModes}. * The value is {@code 0x10}, which does not correspond meaningfully to * any particular {@linkplain Modifier modifier bit}. * In conjunction with the {@code PUBLIC} modifier bit, a {@code Lookup} @@ -96,17 +97,17 @@ public abstract class MethodHandleUtils implements Utils { * lookup class and public types in packages exported by other modules * to the module of the class to be looked up. *

    - * If this lookup mode is set, the {@linkplain MethodHandles.Lookup#previousrequestedClass() + * If this lookup mode is set, the {@linkplain Lookup#previousrequestedClass() * previous lookup class} is always {@code null}. * - * @see MethodHandles.Lookup#MODULE + * @see Lookup#MODULE * @since 9 */ public static final int MODULE = PACKAGE << 1; /** * A single-bit mask representing {@code unconditional} access - * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. + * which may contribute to the result of {@link Lookup#lookupModes lookupModes}. * The value is {@code 0x20}, which does not correspond meaningfully to * any particular {@linkplain Modifier modifier bit}. * A {@code Lookup} with this lookup mode assumes {@linkplain @@ -116,18 +117,18 @@ public abstract class MethodHandleUtils implements Utils { * java.lang.Module#isExported(String) exported unconditionally}. * *

    - * If this lookup mode is set, the {@linkplain MethodHandles.Lookup#previousrequestedClass() + * If this lookup mode is set, the {@linkplain Lookup#previousrequestedClass() * previous lookup class} is always {@code null}. * - * @see MethodHandles.Lookup#publicLookup() - * @see MethodHandles.Lookup#UNCONDITIONAL + * @see Lookup#publicLookup() + * @see Lookup#UNCONDITIONAL * @since 9 */ public static final int UNCONDITIONAL = PACKAGE << 2; /** * A single-bit mask representing {@code original} access - * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. + * which may contribute to the result of {@link Lookup#lookupModes lookupModes}. * The value is {@code 0x40}, which does not correspond meaningfully to * any particular {@linkplain Modifier modifier bit}. * @@ -136,9 +137,9 @@ public abstract class MethodHandleUtils implements Utils { * created by the original lookup class by calling * {@link MethodHandles#lookup()} method or by a bootstrap method * invoked by the VM. The {@code Lookup} object with this lookup - * mode has {@linkplain MethodHandles.Lookup#hasFullPrivilegeAccess() full privilege access}. + * mode has {@linkplain Lookup#hasFullPrivilegeAccess() full privilege access}. * - * @see MethodHandles.Lookup#ORIGINAL + * @see Lookup#ORIGINAL * @since 16 */ public static final int ORIGINAL = PACKAGE << 3; @@ -150,61 +151,61 @@ public abstract class MethodHandleUtils implements Utils { public static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE | MODULE | UNCONDITIONAL | ORIGINAL); /** - * The {@link Constructor} for {@link MethodHandles.Lookup#Lookup(Class)} since JDK 7 + * The {@link Constructor} for {@link Lookup#Lookup(Class)} since JDK 7 */ - private static final Constructor lookupConstructor1 = getDeclaredConstructor(MethodHandles.Lookup.class, Class.class); + private static final Constructor lookupConstructor1 = getDeclaredConstructor(Lookup.class, Class.class); /** - * The {@link Constructor} for {@link MethodHandles.Lookup#Lookup(Class, int)} since JDK 7 + * The {@link Constructor} for {@link Lookup#Lookup(Class, int)} since JDK 7 */ - private static final Constructor lookupConstructor2 = findConstructor(MethodHandles.Lookup.class, Class.class, int.class); + private static final Constructor lookupConstructor2 = findConstructor(Lookup.class, Class.class, int.class); /** - * The {@link Constructor} for {@link MethodHandles.Lookup#Lookup(Class, Class, int)} since JDK 14 + * The {@link Constructor} for {@link Lookup#Lookup(Class, Class, int)} since JDK 14 */ - private static final Constructor lookupConstructor3 = findConstructor(MethodHandles.Lookup.class, Class.class, Class.class, int.class); + private static final Constructor lookupConstructor3 = findConstructor(Lookup.class, Class.class, Class.class, int.class); - private static final ConcurrentMap lookupCache = new ConcurrentHashMap<>(); + private static final ConcurrentMap lookupCache = new ConcurrentHashMap<>(); /** - * The {@link MethodHandles.Lookup} for {@link MethodHandles#publicLookup()} + * The {@link Lookup} for {@link MethodHandles#publicLookup()} */ - public static final MethodHandles.Lookup PUBLIC_LOOKUP = publicLookup(); + public static final Lookup PUBLIC_LOOKUP = publicLookup(); /** - * The allowed {@link MethodHandles.Lookup} modes enumeration + * The allowed {@link Lookup} modes enumeration * - * @see MethodHandles.Lookup#PUBLIC - * @see MethodHandles.Lookup#PRIVATE - * @see MethodHandles.Lookup#PROTECTED - * @see MethodHandles.Lookup#PACKAGE - * @see MethodHandles.Lookup#MODULE - * @see MethodHandles.Lookup#UNCONDITIONAL - * @see MethodHandles.Lookup#ORIGINAL - * @see MethodHandles.Lookup#TRUSTED - * @see MethodHandles.Lookup#ALL_MODES + * @see Lookup#PUBLIC + * @see Lookup#PRIVATE + * @see Lookup#PROTECTED + * @see Lookup#PACKAGE + * @see Lookup#MODULE + * @see Lookup#UNCONDITIONAL + * @see Lookup#ORIGINAL + * @see Lookup#TRUSTED + * @see Lookup#ALL_MODES */ public enum LookupMode { /** - * @see MethodHandles.Lookup#PUBLIC + * @see Lookup#PUBLIC */ - PUBLIC(MethodHandles.Lookup.PUBLIC), + PUBLIC(Lookup.PUBLIC), /** - * @see MethodHandles.Lookup#PRIVATE + * @see Lookup#PRIVATE */ - PRIVATE(MethodHandles.Lookup.PRIVATE), + PRIVATE(Lookup.PRIVATE), /** - * @see MethodHandles.Lookup#PROTECTED + * @see Lookup#PROTECTED */ - PROTECTED(MethodHandles.Lookup.PROTECTED), + PROTECTED(Lookup.PROTECTED), /** - * @see MethodHandles.Lookup#PACKAGE + * @see Lookup#PACKAGE */ - PACKAGE(MethodHandles.Lookup.PACKAGE), + PACKAGE(Lookup.PACKAGE), /** * @see MethodHandleUtils#MODULE @@ -283,31 +284,31 @@ static LookupKey buildKey(Class requestedClass, int allowedModes) { } /** - * Create an instance of {@link MethodHandles.Lookup} by the specified lookup class + * Create an instance of {@link Lookup} by the specified lookup class * with {@link #ALL_MODES all accesses (public, private, protected and package)} * * @param requestedClass the class to be looked up * @return non-null */ - public static MethodHandles.Lookup lookup(Class requestedClass) { + public static Lookup lookup(Class requestedClass) { return lookup(requestedClass, LookupMode.ALL); } /** - * Create an instance of {@link MethodHandles.Lookup} by the specified lookup class + * Create an instance of {@link Lookup} by the specified lookup class * with {@link #ALL_MODES all access (public, private, protected and package)} * * @param requestedClass the class to be looked up * @return non-null */ - public static MethodHandles.Lookup lookup(Class requestedClass, LookupMode... lookupModes) { + public static Lookup lookup(Class requestedClass, LookupMode... lookupModes) { int allowedModes = getModes(lookupModes); LookupKey key = buildKey(requestedClass, allowedModes); return lookupCache.computeIfAbsent(key, MethodHandleUtils::newLookup); } /** - * The convenient method to find {@link MethodHandles.Lookup#findVirtual(Class, String, MethodType)} + * The convenient method to find {@link Lookup#findVirtual(Class, String, MethodType)} * * @param requestedClass the class to be looked up * @param methodName the target method name @@ -319,7 +320,7 @@ public static MethodHandle findVirtual(Class requestedClass, String methodNam } /** - * The convenient method to find {@link MethodHandles.Lookup#findStatic(Class, String, MethodType)} + * The convenient method to find {@link Lookup#findStatic(Class, String, MethodType)} * * @param requestedClass the class to be looked up * @param methodName the target method name @@ -342,7 +343,7 @@ public static void handleInvokeExactFailure(Throwable e, MethodHandle methodHand } protected static MethodHandle find(Class requestedClass, String methodName, Class[] parameterTypes, - ThrowableBiFunction function) { + ThrowableBiFunction function) { Method method = findMethod(requestedClass, methodName, parameterTypes); if (method == null) { return NOT_FOUND_METHOD_HANDLE; @@ -350,11 +351,11 @@ protected static MethodHandle find(Class requestedClass, String methodName, C if (isiCandidateMethod(method)) { return findPublic(method, function); } - MethodHandles.Lookup lookup = lookup(requestedClass); + Lookup lookup = lookup(requestedClass); return MethodHandlesLookupUtils.find(lookup, requestedClass, methodName, parameterTypes, function); } - private static MethodHandles.Lookup newLookup(LookupKey key) { + private static Lookup newLookup(LookupKey key) { if (lookupConstructor3 != null) { return newInstance(lookupConstructor3, key.requestedClass, key.requestedClass, key.allowedModes); } diff --git a/microsphere-java-core/src/main/java/io/microsphere/logging/JDKLoggerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/logging/JDKLoggerFactory.java index 3ae3bdbd2..0606ba4e2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/logging/JDKLoggerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/logging/JDKLoggerFactory.java @@ -20,6 +20,12 @@ import java.util.logging.Level; +import static java.util.logging.Level.ALL; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Level.WARNING; + /** * A {@link LoggerFactory} implementation that creates and manages instances of JDK logging ({@link java.util.logging.Logger}). * @@ -75,77 +81,77 @@ static class JDKLogger extends AbstractLogger { @Override public boolean isTraceEnabled() { - return isLoggable(Level.ALL); + return isLoggable(ALL); } @Override public void trace(String message) { - log(Level.ALL, message); + log(ALL, message); } @Override public void trace(String message, Throwable t) { - log(Level.ALL, message, t); + log(ALL, message, t); } @Override public boolean isDebugEnabled() { - return isLoggable(Level.FINE); + return isLoggable(FINE); } @Override public void debug(String message) { - log(Level.FINE, message); + log(FINE, message); } @Override public void debug(String message, Throwable t) { - log(Level.FINE, message, t); + log(FINE, message, t); } @Override public boolean isInfoEnabled() { - return this.isLoggable(Level.INFO); + return this.isLoggable(INFO); } @Override public void info(String message) { - log(Level.INFO, message); + log(INFO, message); } @Override public void info(String message, Throwable t) { - log(Level.INFO, message, t); + log(INFO, message, t); } @Override public boolean isWarnEnabled() { - return isLoggable(Level.WARNING); + return isLoggable(WARNING); } @Override public void warn(String message) { - log(Level.WARNING, message); + log(WARNING, message); } @Override public void warn(String message, Throwable t) { - log(Level.WARNING, message, t); + log(WARNING, message, t); } @Override public boolean isErrorEnabled() { - return isLoggable(Level.SEVERE); + return isLoggable(SEVERE); } @Override public void error(String message) { - log(Level.SEVERE, message); + log(SEVERE, message); } @Override public void error(String message, Throwable t) { - log(Level.SEVERE, message, t); + log(SEVERE, message, t); } boolean isLoggable(Level level) { diff --git a/microsphere-java-core/src/main/java/io/microsphere/logging/LoggerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/logging/LoggerFactory.java index 2adadb1ed..2d45c3f5f 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/logging/LoggerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/logging/LoggerFactory.java @@ -83,7 +83,7 @@ static List loadAvailableFactories() { static List loadFactories() { List factories = newLinkedList(load(LoggerFactory.class, classLoader)); - sort(factories, Prioritized.COMPARATOR); + sort(factories, COMPARATOR); return factories; } diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactory.java b/microsphere-java-core/src/main/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactory.java index 9de84327b..e8baf1ad7 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactory.java @@ -25,6 +25,7 @@ import java.util.List; import static io.microsphere.collection.ListUtils.newLinkedList; +import static io.microsphere.lang.Prioritized.COMPARATOR; import static java.util.Collections.sort; /** @@ -116,7 +117,7 @@ public boolean remove(SubProtocolURLConnectionFactory factory) { private void sortFactories() { List factories = this.factories; - sort(factories, Prioritized.COMPARATOR); + sort(factories, COMPARATOR); } @Override diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/CompositeURLStreamHandlerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/net/CompositeURLStreamHandlerFactory.java index 9db1e85e2..8ec69fc6b 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/CompositeURLStreamHandlerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/CompositeURLStreamHandlerFactory.java @@ -25,6 +25,7 @@ import java.util.List; import static io.microsphere.collection.ListUtils.newLinkedList; +import static io.microsphere.lang.Prioritized.COMPARATOR; import static java.util.Collections.emptyList; import static java.util.Collections.sort; @@ -119,7 +120,7 @@ protected List getFactories() { * @return {@link Prioritized#COMPARATOR} as default */ protected Comparator getComparator() { - return Prioritized.COMPARATOR; + return COMPARATOR; } private void sortFactories(List factories) { diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/ExtendableProtocolURLStreamHandler.java b/microsphere-java-core/src/main/java/io/microsphere/net/ExtendableProtocolURLStreamHandler.java index 5d0dc0ef4..91ae079cd 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/ExtendableProtocolURLStreamHandler.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/ExtendableProtocolURLStreamHandler.java @@ -18,7 +18,6 @@ import io.microsphere.annotation.Immutable; import io.microsphere.annotation.Nonnull; -import io.microsphere.lang.Prioritized; import java.io.IOException; import java.net.Proxy; @@ -36,6 +35,7 @@ import static io.microsphere.constants.SymbolConstants.DOT_CHAR; import static io.microsphere.constants.SymbolConstants.QUERY_STRING; import static io.microsphere.constants.SymbolConstants.QUOTE; +import static io.microsphere.lang.Prioritized.COMPARATOR; import static io.microsphere.net.URLUtils.DEFAULT_HANDLER_PACKAGE_PREFIX; import static io.microsphere.net.URLUtils.HANDLER_CONVENTION_CLASS_NAME; import static io.microsphere.net.URLUtils.HANDLER_PACKAGES_PROPERTY_NAME; @@ -240,7 +240,7 @@ public void init() { private void initSubProtocolURLConnectionFactories() { List factories = this.factories; initSubProtocolURLConnectionFactories(factories); - sort(factories, Prioritized.COMPARATOR); + sort(factories, COMPARATOR); } /** diff --git a/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java index 3c4bac0c8..a978f1b6f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java @@ -44,6 +44,8 @@ import static io.microsphere.collection.Sets.ofSet; import static io.microsphere.io.event.FileChangedEvent.Kind.MODIFIED; import static io.microsphere.lang.MutableInteger.of; +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -117,7 +119,7 @@ void testResolvePropertiesAsMap() { assertEquals("test", propertiesMap.get("name")); assertEquals("value", propertiesMap.get("value")); assertEquals("defaultValue", propertiesMap.get("defaultValue")); - assertEquals(Boolean.TRUE, propertiesMap.get("required")); + assertEquals(TRUE, propertiesMap.get("required")); assertEquals("description", propertiesMap.get("description")); Map metadataMap = (Map) propertiesMap.get("metadata"); @@ -131,7 +133,7 @@ void testResolvePropertiesAsMap() { void testResolvePropertiesAsMapWithTestBean() { TestBean testBean = new TestBean(); testBean.booleanValue = true; - testBean.booleanObject = Boolean.FALSE; + testBean.booleanObject = FALSE; testBean.string = "string"; testBean.stringBuilder = new StringBuilder("stringBuilder"); testBean.atomicInteger = new AtomicInteger(1); @@ -150,7 +152,7 @@ void testResolvePropertiesAsMapWithTestBean() { Map propertiesMap = resolvePropertiesAsMap(testBean); assertEquals(true, propertiesMap.get("booleanValue")); - assertEquals(Boolean.FALSE, propertiesMap.get("booleanObject")); + assertEquals(FALSE, propertiesMap.get("booleanObject")); assertEquals("string", propertiesMap.get("string")); assertEquals("stringBuilder", propertiesMap.get("stringBuilder").toString()); assertEquals(1, ((AtomicInteger) propertiesMap.get("atomicInteger")).get()); diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/StringToBooleanConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/StringToBooleanConverterTest.java index f68938fce..cd8eb4818 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/StringToBooleanConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/StringToBooleanConverterTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import static io.microsphere.convert.StringToBooleanConverter.INSTANCE; +import static java.lang.Boolean.TRUE; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -42,7 +43,7 @@ protected String getSource() throws Throwable { @Override protected Boolean getTarget() throws Throwable { - return Boolean.TRUE; + return TRUE; } @Test diff --git a/microsphere-java-core/src/test/java/io/microsphere/json/JSONUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/json/JSONUtilsTest.java index d50fa6507..8cfa138a9 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/json/JSONUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/json/JSONUtilsTest.java @@ -70,6 +70,7 @@ import static java.lang.Boolean.TRUE; import static java.lang.Byte.valueOf; import static java.lang.Character.valueOf; +import static java.lang.Void.TYPE; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.HOURS; import static java.util.concurrent.TimeUnit.MINUTES; @@ -387,7 +388,7 @@ void testIsUnknownClass() { assertTrue(isUnknownClass(Void.class)); assertFalse(isUnknownClass(String.class)); - assertFalse(isUnknownClass(Void.TYPE)); + assertFalse(isUnknownClass(TYPE)); assertFalse(isUnknownClass(null)); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/logging/LoggerFactoryTest.java b/microsphere-java-core/src/test/java/io/microsphere/logging/LoggerFactoryTest.java index 5a08f44c4..808572d04 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/logging/LoggerFactoryTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/logging/LoggerFactoryTest.java @@ -22,11 +22,12 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.util.Calendar; import java.util.logging.LogManager; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.logging.LoggerFactory.loadAvailableFactories; +import static java.util.Calendar.YEAR; +import static java.util.Calendar.getInstance; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -65,7 +66,7 @@ private void testLoggerFactory(LoggerFactory loggerFactory) { private void log(Logger logger) { assertLevel(logger); log(logger, "Hello,World"); - log(logger, "Hello,World {}", Calendar.getInstance().get(Calendar.YEAR)); + log(logger, "Hello,World {}", getInstance().get(YEAR)); log(logger, "Hello,World", new Throwable("Testing")); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/CompositeURLStreamHandlerFactoryTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/CompositeURLStreamHandlerFactoryTest.java index 219f7b572..4eb7e94d5 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/CompositeURLStreamHandlerFactoryTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/CompositeURLStreamHandlerFactoryTest.java @@ -1,6 +1,5 @@ package io.microsphere.net; -import io.microsphere.lang.Prioritized; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -9,6 +8,7 @@ import java.util.List; import static io.microsphere.constants.ProtocolConstants.FILE_PROTOCOL; +import static io.microsphere.lang.Prioritized.COMPARATOR; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; @@ -67,7 +67,7 @@ void testGetFactories() { @Test void testGetComparator() { - assertSame(Prioritized.COMPARATOR, compositeFactory.getComparator()); + assertSame(COMPARATOR, compositeFactory.getComparator()); } @Test diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/JavaTypeTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/JavaTypeTest.java index 3e624efeb..359479cd7 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/JavaTypeTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/JavaTypeTest.java @@ -16,6 +16,7 @@ */ package io.microsphere.reflect; +import io.microsphere.reflect.JavaType.Kind; import io.microsphere.test.StringIntegerBooleanHashMap; import io.microsphere.test.StringIntegerHashMap; import org.junit.jupiter.api.Test; @@ -30,6 +31,7 @@ import java.util.Map; import static io.microsphere.reflect.JavaType.EMPTY_JAVA_TYPE_ARRAY; +import static io.microsphere.reflect.JavaType.Kind.CLASS; import static io.microsphere.reflect.JavaType.NULL_JAVA_TYPE; import static io.microsphere.reflect.JavaType.from; import static io.microsphere.reflect.JavaType.fromField; @@ -246,7 +248,7 @@ private static void assertGenericTypes(JavaType javaType, Class... expectedCl private static void assertJavaType(JavaType javaType) { // assert JavaType.Kind == PARAMETERIZED_TYPE - assertEquals(JavaType.Kind.PARAMETERIZED_TYPE, javaType.getKind()); + assertEquals(Kind.PARAMETERIZED_TYPE, javaType.getKind()); // assert Type == ParameterizedType Type fieldType = javaType.getType(); assertTrue(ParameterizedType.class.isInstance(fieldType)); @@ -267,7 +269,7 @@ private static void assertJavaType(JavaType javaType) { // as Cloneable -> JavaType Cloneable : Interface type - Cloneable.class JavaType targetType = javaType.as(Cloneable.class); - assertEquals(JavaType.Kind.CLASS, targetType.getKind()); + assertEquals(CLASS, targetType.getKind()); assertEquals(Cloneable.class, targetType.getRawType()); assertEquals(Cloneable.class, targetType.toClass()); // assert source @@ -298,12 +300,12 @@ private static void assertJavaType(JavaType javaType) { assertEquals(2, genericJavaTypes.length); // First - Integer.class JavaType genericJavaType0 = genericJavaTypes[0]; - assertEquals(JavaType.Kind.CLASS, genericJavaType0.getKind()); + assertEquals(CLASS, genericJavaType0.getKind()); assertEquals(Integer.class, genericJavaType0.getType()); // Second - ParameterizedType(List>) // Raw Type == java.util.List JavaType genericJavaType1 = genericJavaTypes[1]; - assertEquals(JavaType.Kind.PARAMETERIZED_TYPE, genericJavaType1.getKind()); + assertEquals(Kind.PARAMETERIZED_TYPE, genericJavaType1.getKind()); Type genericRawType1 = genericJavaType1.getRawType(); assertEquals(List.class, genericRawType1); // genericType1 == ParameterizedType(List>) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java index ad701ede8..c09542091 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java @@ -24,6 +24,7 @@ import java.util.concurrent.locks.AbstractQueuedSynchronizer; import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; +import static io.microsphere.reflect.Modifier.MANDATED; import static io.microsphere.reflect.Modifier.isAbstract; import static io.microsphere.reflect.Modifier.isAnnotation; import static io.microsphere.reflect.Modifier.isBridge; @@ -191,7 +192,7 @@ void testIsEnum() { @Test void testIsMandated() { - assertTrue(isMandated(Modifier.MANDATED.getValue())); + assertTrue(isMandated(MANDATED.getValue())); assertFalse(isMandated(Object.class.getModifiers())); } diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/model/SimpleTypeModelTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/model/SimpleTypeModelTest.java index 6a0d24251..bf49575bd 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/model/SimpleTypeModelTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/model/SimpleTypeModelTest.java @@ -7,6 +7,8 @@ import java.math.BigInteger; import java.util.Date; +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -49,12 +51,12 @@ void testSetGetVoid() { @Test void testSetGetBoolean() { - Boolean testValue = Boolean.TRUE; + Boolean testValue = TRUE; model.setZ(testValue); assertEquals(testValue, model.getZ(), "getZ should return the set value"); - model.setZ(Boolean.FALSE); - assertEquals(Boolean.FALSE, model.getZ(), "getZ should return the set value"); + model.setZ(FALSE); + assertEquals(FALSE, model.getZ(), "getZ should return the set value"); } @Test From 54646dedddadf2e7559e8d080029eef005568ffa Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:24:34 +0800 Subject: [PATCH 174/438] Add null-check for iterator.next() in test Improve DelegatingIteratorTest by importing assertNotNull and asserting that delegatingIterator.next() returns a non-null value before calling remove(). This makes the test more explicit about the expected non-null elements when iterating and removing. --- .../java/io/microsphere/collection/DelegatingIteratorTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingIteratorTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingIteratorTest.java index f29382d37..b9a197f00 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingIteratorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingIteratorTest.java @@ -28,6 +28,7 @@ import static io.microsphere.collection.ListUtils.ofArrayList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -68,7 +69,7 @@ void testNext() { @Test void testRemove() { while (this.delegatingIterator.hasNext()) { - this.delegatingIterator.next(); + assertNotNull(this.delegatingIterator.next()); this.delegatingIterator.remove(); } } From df9a0b53ccb01d36038bd59e5a09bd0c1a8bbca7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:26:35 +0800 Subject: [PATCH 175/438] Add null check in AbstractConverterTest Add an assertNotNull import and assertion in testConvertIfPossible to verify that the converter instance is initialized before running further tests. This strengthens the test by explicitly checking for a non-null converter. --- .../test/java/io/microsphere/convert/AbstractConverterTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java index 26bf13fa1..08b924c4a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/AbstractConverterTest.java @@ -23,6 +23,7 @@ import static io.microsphere.lang.Prioritized.NORMAL_PRIORITY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; /** @@ -73,6 +74,7 @@ void testGetPriority() { @Test void testConvertIfPossible() { + assertNotNull(this.converter); } @Test From 2ff3625143f0e2ab3c29aa7ed35b63911d3a5d30 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:34:02 +0800 Subject: [PATCH 176/438] Assert link exists after deleteDirectoryOnExit Add assertions in FileUtilsTest to verify the link file still exists after calling deleteDirectoryOnExit on both the link and the temp directory. This makes the test explicit about expected state after scheduling deletions and helps catch regressions where cleanup might remove files immediately. --- .../src/test/java/io/microsphere/io/FileUtilsTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index 5980784f5..b0bb1bf39 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -268,6 +268,7 @@ void testDeleteDirectoryOnExit() throws Exception { File link = makeLinkFile(tempDir); deleteDirectoryOnExit(link); + assertTrue(link.exists()); for (int i = 0; i < 10; i++) { if (i % 2 == 0) { @@ -276,7 +277,9 @@ void testDeleteDirectoryOnExit() throws Exception { createRandomFile(tempDir); } } + deleteDirectoryOnExit(tempDir); + assertTrue(link.exists()); } @Test From 06b6ca50243e3a7f67396401ba1b4419b1667e26 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:34:08 +0800 Subject: [PATCH 177/438] Use assertDoesNotThrow in test Update AccessibleObjectUtilsTest to explicitly assert that handleInaccessibleObjectExceptionIfFound does not throw an exception by wrapping the call with assertDoesNotThrow and adding the required static import. This makes the test intention clearer and ensures failures are reported by the test framework. --- .../java/io/microsphere/reflect/AccessibleObjectUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/AccessibleObjectUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/AccessibleObjectUtilsTest.java index 18e4bc7d2..1819ef404 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/AccessibleObjectUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/AccessibleObjectUtilsTest.java @@ -31,6 +31,7 @@ import static io.microsphere.reflect.ConstructorUtils.findConstructor; import static io.microsphere.reflect.MemberUtils.isStatic; import static io.microsphere.reflect.MethodUtils.findMethod; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -119,6 +120,6 @@ void testTryCanAccessOnFailed() { @Test void testHandleInaccessibleObjectExceptionIfFound() { - handleInaccessibleObjectExceptionIfFound(new RuntimeException("For testing")); + assertDoesNotThrow(() -> handleInaccessibleObjectExceptionIfFound(new RuntimeException("For testing"))); } } \ No newline at end of file From 6204c3a63331b0a096d351838bc53132888cd188 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:35:51 +0800 Subject: [PATCH 178/438] Remove ReflectiveDefinitionTest Delete the obsolete empty unit test class io/microsphere/reflect/ReflectiveDefinitionTest. The file contained only placeholder JUnit test methods with no assertions or logic, so it was removed to clean up unused test artifacts. --- .../reflect/ReflectiveDefinitionTest.java | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java deleted file mode 100644 index 13cdee6ac..000000000 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - */ - -package io.microsphere.reflect; - - -import org.junit.jupiter.api.Test; - -/** - * {@link ReflectiveDefinition} Test - * - * @author Mercy - * @see ReflectiveDefinition - * @since 1.0.0 - */ -class ReflectiveDefinitionTest { - - @Test - void testGetSince() { - } - - @Test - void testGetDeprecation() { - } - - @Test - void testGetClassName() { - } - - @Test - void testGetResolvedClass() { - } - - @Test - void testIsDeprecated() { - } - - @Test - void testIsPresent() { - } - - @Test - void testTestEquals() { - } - - @Test - void testTestHashCode() { - } - - @Test - void testTestToString() { - } -} \ No newline at end of file From 438c12985620cc60169b0116bae55e61f8035dc3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:37:47 +0800 Subject: [PATCH 179/438] Add assertNotNull in testOnFailure Add a static import for assertNotNull and call assertNotNull(AnnotationProcessingTest.class) in testOnFailure before throwing the RuntimeException. This makes the test include an explicit assertion (and prevents an unused-import/warning) while preserving the original failure behavior. --- .../test/annotation/processing/AnnotationProcessingTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java index 9b40f9b77..7696da277 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/annotation/processing/AnnotationProcessingTest.java @@ -31,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; /** @@ -76,6 +77,7 @@ void test() { @Test @ExtendWith(AnnotationProcessingTest.class) void testOnFailure() { + assertNotNull(AnnotationProcessingTest.class); throw new RuntimeException("For testing"); } From af68b7c5109799ea7c1f458e986c015970c1330b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:40:10 +0800 Subject: [PATCH 180/438] Uncomment model test; add Model and assertSame imports Enable and assert behavior of inherited model method by uncommenting the test that constructs a Model, calls genericTestService.model(inputModel), and verifies the same instance is returned using assertSame. Added import for io.microsphere.test.model.Model and static import for assertSame. Also adjusted formatting of the assertTrue assertion for consistency. --- .../microsphere/test/service/GenericTestServiceTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-java-test/src/test/java/io/microsphere/test/service/GenericTestServiceTest.java b/microsphere-java-test/src/test/java/io/microsphere/test/service/GenericTestServiceTest.java index d97ca6011..923bde574 100644 --- a/microsphere-java-test/src/test/java/io/microsphere/test/service/GenericTestServiceTest.java +++ b/microsphere-java-test/src/test/java/io/microsphere/test/service/GenericTestServiceTest.java @@ -1,9 +1,11 @@ package io.microsphere.test.service; +import io.microsphere.test.model.Model; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -39,11 +41,9 @@ void testInheritedMethods() { // Example: Testing a method inherited from DefaultTestService // Assuming DefaultTestService has a method like model(Model model) // Uncomment and adapt the following lines if such a method exists: - /* Model inputModel = new Model(); Model outputModel = genericTestService.model(inputModel); assertSame(inputModel, outputModel, "The inherited model method should return the same Model object."); - */ } /** @@ -52,7 +52,7 @@ void testInheritedMethods() { */ @Test void testEventListenerImplementation() { - assertTrue(genericTestService instanceof java.util.EventListener, - "GenericTestService should implement the EventListener interface."); + assertTrue(genericTestService instanceof java.util.EventListener, + "GenericTestService should implement the EventListener interface."); } } From 95b8da9d1e71377f03c98072efa01533f6c0cfea Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:42:49 +0800 Subject: [PATCH 181/438] Wrap logger tests with assertDoesNotThrow Update LoggerUtilsTest to import assertDoesNotThrow and wrap all LoggerUtils calls (trace, debug, info, warn, error) in assertDoesNotThrow lambdas. This makes the tests explicitly verify that logging calls do not throw exceptions and improves test robustness. --- .../lang/model/util/LoggerUtilsTest.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/LoggerUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/LoggerUtilsTest.java index 7f916931b..e2874a89e 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/LoggerUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/LoggerUtilsTest.java @@ -24,6 +24,7 @@ import static io.microsphere.lang.model.util.LoggerUtils.info; import static io.microsphere.lang.model.util.LoggerUtils.trace; import static io.microsphere.lang.model.util.LoggerUtils.warn; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNotNull; /** @@ -41,36 +42,36 @@ void testLogger() { @Test void testTrace() { - trace("Hello,World"); - trace("Hello,{}", "World"); - trace("{},{}", "Hello", "World"); + assertDoesNotThrow(() -> trace("Hello,World")); + assertDoesNotThrow(() -> trace("Hello,{}", "World")); + assertDoesNotThrow(() -> trace("{},{}", "Hello", "World")); } @Test void testDebug() { - debug("Hello,World"); - debug("Hello,{}", "World"); - debug("{},{}", "Hello", "World"); + assertDoesNotThrow(() -> debug("Hello,World")); + assertDoesNotThrow(() -> debug("Hello,{}", "World")); + assertDoesNotThrow(() -> debug("{},{}", "Hello", "World")); } @Test void testInfo() { - info("Hello,World"); - info("Hello,{}", "World"); - info("{},{}", "Hello", "World"); + assertDoesNotThrow(() -> info("Hello,World")); + assertDoesNotThrow(() -> info("Hello,{}", "World")); + assertDoesNotThrow(() -> info("{},{}", "Hello", "World")); } @Test void testWarn() { - warn("Hello,World"); - warn("Hello,{}", "World"); - warn("{},{}", "Hello", "World"); + assertDoesNotThrow(() -> warn("Hello,World")); + assertDoesNotThrow(() -> warn("Hello,{}", "World")); + assertDoesNotThrow(() -> warn("{},{}", "Hello", "World")); } @Test void testError() { - error("Hello,World"); - error("Hello,{}", "World"); - error("{},{}", "Hello", "World"); + assertDoesNotThrow(() -> error("Hello,World")); + assertDoesNotThrow(() -> error("Hello,{}", "World")); + assertDoesNotThrow(() -> error("{},{}", "Hello", "World")); } } From 48ffc439c621c001d13f02714bba2bd3ddc85ae7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:44:16 +0800 Subject: [PATCH 182/438] Use assertDoesNotThrow in MessagerUtilsTest Wrap MessagerUtils test calls in assertDoesNotThrow to explicitly assert they don't throw exceptions. Adds a static import for assertDoesNotThrow and updates tests for printNote, printWarning, printMandatoryWarning, printError, and printMessage to invoke both processingEnv and messager variants inside lambdas. --- .../lang/model/util/MessagerUtilsTest.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java index 554d4b4d2..f76759559 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MessagerUtilsTest.java @@ -31,6 +31,7 @@ import static io.microsphere.lang.model.util.MessagerUtils.printNote; import static io.microsphere.lang.model.util.MessagerUtils.printWarning; import static javax.tools.Diagnostic.Kind.OTHER; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; /** * {@link MessagerUtils} Test @@ -52,31 +53,31 @@ protected void beforeTest(ReflectiveInvocationContext invocationContext, @Test void testPrintNote() { - printNote(this.processingEnv, "Hello, {}!", "printNote"); - printNote(this.messager, "Hello, {}!", "printNote"); + assertDoesNotThrow(() -> printNote(this.processingEnv, "Hello, {}!", "printNote")); + assertDoesNotThrow(() -> printNote(this.messager, "Hello, {}!", "printNote")); } @Test void testPrintWarning() { - printWarning(this.processingEnv, "Hello, {}!", "printWarning"); - printWarning(this.messager, "Hello, {}!", "printWarning"); + assertDoesNotThrow(() -> printWarning(this.processingEnv, "Hello, {}!", "printWarning")); + assertDoesNotThrow(() -> printWarning(this.messager, "Hello, {}!", "printWarning")); } @Test void testPrintMandatoryWarning() { - printMandatoryWarning(this.processingEnv, "Hello, {}!", "printMandatoryWarning"); - printMandatoryWarning(this.messager, "Hello, {}!", "printMandatoryWarning"); + assertDoesNotThrow(() -> printMandatoryWarning(this.processingEnv, "Hello, {}!", "printMandatoryWarning")); + assertDoesNotThrow(() -> printMandatoryWarning(this.messager, "Hello, {}!", "printMandatoryWarning")); } @Test void testPrintError() { - printError(this.processingEnv, "Hello, {}!", "printError"); - printError(this.messager, "Hello, {}!", "printError"); + assertDoesNotThrow(() -> printError(this.processingEnv, "Hello, {}!", "printError")); + assertDoesNotThrow(() -> printError(this.messager, "Hello, {}!", "printError")); } @Test void testPrintMessage() { - printMessage(this.processingEnv, OTHER, "Hello, {}!", "printMessage"); - printMessage(this.messager, OTHER, "Hello, {}!", "printMessage"); + assertDoesNotThrow(() -> printMessage(this.processingEnv, OTHER, "Hello, {}!", "printMessage")); + assertDoesNotThrow(() -> printMessage(this.messager, OTHER, "Hello, {}!", "printMessage")); } } \ No newline at end of file From 2f0b132cdc33a7db9b7f0a17b92f53f52366f494 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:45:05 +0800 Subject: [PATCH 183/438] Format call and remove empty test Fix spacing in super.beforeTest invocation for readability and remove an unused empty test method (testFilterMethods) from MethodUtilsTest. No functional changes intended; tidies up test code. --- .../io/microsphere/lang/model/util/MethodUtilsTest.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java index 67a87679d..1c760eb0f 100644 --- a/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java +++ b/microsphere-lang-model/src/test/java/io/microsphere/lang/model/util/MethodUtilsTest.java @@ -86,7 +86,7 @@ protected void addCompiledClasses(Set> compiledClasses) { @Override protected void beforeTest(ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { - super.beforeTest(invocationContext,extensionContext); + super.beforeTest(invocationContext, extensionContext); TypeElement type = getTypeElement(Object.class); List methods = getDeclaredMethods(type); this.objectMethods = methods; @@ -354,11 +354,6 @@ void testGetOverrideMethod() { assertEquals(methods.get(0), overrideMethod); } - @Test - void testFilterMethods() { - - } - @Test void testFilterMethodsOnNull() { assertEmptyList(filterMethods(NULL_LIST, alwaysTrue())); From 3a7250add1d5666d1badbf0f959954d6f1b8debc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:48:31 +0800 Subject: [PATCH 184/438] Use assertDoesNotThrow in JarUtilsTest Replace test methods that declared throws IOException with JUnit 5 assertDoesNotThrow wrappers to explicitly assert no exceptions are thrown during extraction. Adds static import for assertDoesNotThrow and updates testExtractWithURL, testDoExtractOnNullJarFile, testDoExtractWithoutJarEntry, and testDoExtractOnMissingMatch to remove throws declarations and wrap calls in lambda assertions. --- .../io/microsphere/util/jar/JarUtilsTest.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java index 33e11b93b..4a90ec23e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java @@ -33,6 +33,7 @@ import static io.microsphere.util.jar.JarUtils.resolveRelativePath; import static io.microsphere.util.jar.JarUtils.toJarFile; import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -135,30 +136,30 @@ void testExtract() throws IOException { } @Test - void testExtractWithURL() throws IOException { - extract(this.resourceURL, this.targetDirectory, (JarEntryFilter) filteredObject -> !filteredObject.isDirectory()); + void testExtractWithURL() { + assertDoesNotThrow(() -> extract(this.resourceURL, this.targetDirectory, (JarEntryFilter) filteredObject -> !filteredObject.isDirectory())); URL resourceURL = getResource(this.classLoader, PACKAGE, "javax.annotation"); - extract(resourceURL, this.targetDirectory, (JarEntryFilter) filteredObject -> !filteredObject.isDirectory()); + assertDoesNotThrow(() -> extract(resourceURL, this.targetDirectory, (JarEntryFilter) filteredObject -> !filteredObject.isDirectory())); } @Test - void testDoExtractOnNullJarFile() throws IOException { - doExtract(null, null, this.targetDirectory); + void testDoExtractOnNullJarFile() { + assertDoesNotThrow(() -> doExtract(null, null, this.targetDirectory)); } @Test - void testDoExtractWithoutJarEntry() throws IOException { + void testDoExtractWithoutJarEntry() { JarFile jarFile = toJarFile(this.resourceURL); - doExtract(jarFile, null, this.targetDirectory); + assertDoesNotThrow(() -> doExtract(jarFile, null, this.targetDirectory)); } @Test - void testDoExtractOnMissingMatch() throws IOException { + void testDoExtractOnMissingMatch() { URL resourceURL = getClassResource(this.classLoader, Test.class); JarFile jarFile = toJarFile(resourceURL); List jarEntries = filter(jarFile, null); - doExtract(toJarFile(this.resourceURL), jarEntries, this.targetDirectory); + assertDoesNotThrow(() -> doExtract(toJarFile(this.resourceURL), jarEntries, this.targetDirectory)); } @Test From 9c62c1717ef5bf538e7f4119f82738df74bdfeda Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 21:59:42 +0800 Subject: [PATCH 185/438] Use equals() for empty string check Replace reference equality (EMPTY == archiveEntryPath) with content-based comparison (EMPTY.equals(archiveEntryPath)) in doResolveArchiveEntryPath. This ensures correct detection of an empty archive entry path rather than relying on reference equality. --- .../src/main/java/io/microsphere/net/URLUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index c1701981d..8f23b03f0 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -214,7 +214,7 @@ public static String resolveArchiveEntryPath(URL archiveFileURL) throws NullPoin protected static String doResolveArchiveEntryPath(String path) { String archiveEntryPath = substringAfter(path, ARCHIVE_ENTRY_SEPARATOR); - return EMPTY == archiveEntryPath ? null : decode(archiveEntryPath); + return EMPTY.equals(archiveEntryPath) ? null : decode(archiveEntryPath); } /** From 2272aca88517edcd5a56cf4957c3d91ccf547e82 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 22:03:28 +0800 Subject: [PATCH 186/438] Wrap all exceptions when copying process output Replace the multi-catch of InterruptedException and ExecutionException with a single catch(Exception) so any exception (including TimeoutException) from Future.get(...) is wrapped into an IOException. Also removed the now-unused ExecutionException import. --- .../src/main/java/io/microsphere/process/ProcessExecutor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java index 5c49e3f10..e2dc44948 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -184,7 +183,7 @@ public void execute(OutputStream outputStream, long timeout, TimeUnit timeUnit) try { byte[] bytes = future.get(timeout, timeUnit); copy(new FastByteArrayInputStream(bytes), outputStream); - } catch (InterruptedException | ExecutionException e) { + } catch (Exception e) { throw wrap(e, IOException.class); } } From 2d94124fdef2fde79adb1ef29acf4a020d735a98 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Feb 2026 22:12:13 +0800 Subject: [PATCH 187/438] Preserve TimeoutException in ProcessExecutor Do not wrap TimeoutException into IOException when reading process output. Added an instanceof check in the catch block to rethrow TimeoutException directly so callers can detect timeouts instead of receiving a wrapped IOException. --- .../src/main/java/io/microsphere/process/ProcessExecutor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java index e2dc44948..043b5d155 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java @@ -184,6 +184,9 @@ public void execute(OutputStream outputStream, long timeout, TimeUnit timeUnit) byte[] bytes = future.get(timeout, timeUnit); copy(new FastByteArrayInputStream(bytes), outputStream); } catch (Exception e) { + if (e instanceof TimeoutException) { + throw (TimeoutException) e; + } throw wrap(e, IOException.class); } } From bfe7efa9bd3d3d9ffed98e858515852c5e89bba2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 09:58:14 +0800 Subject: [PATCH 188/438] Document Compatible and refactor imports Add comprehensive Javadoc to Compatible (constructor, of(Class), on(...), call(), accept(), get()) and include usage examples. Refactor imports to use static Version.getVersion/ofVersion and import Version.Operator as Operator, replacing fully-qualified Version references. These are documentation and import cleanups only; no functional behavior changes. --- .../java/io/microsphere/util/Compatible.java | 159 +++++++++++++++++- 1 file changed, 152 insertions(+), 7 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/Compatible.java b/microsphere-java-core/src/main/java/io/microsphere/util/Compatible.java index c292752e8..ad78cbee2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/Compatible.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/Compatible.java @@ -17,11 +17,14 @@ package io.microsphere.util; import io.microsphere.annotation.Nullable; +import io.microsphere.util.Version.Operator; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; +import static io.microsphere.util.Version.getVersion; +import static io.microsphere.util.Version.ofVersion; import static java.lang.Boolean.TRUE; import static java.util.Optional.ofNullable; @@ -75,7 +78,7 @@ * @param the type of context object associated with this compatibility check (not used directly in logic) * @param the return type of the conditional function * @see Version - * @see Version.Operator + * @see Operator */ public class Compatible { @@ -83,21 +86,111 @@ public class Compatible { private final Function conditionalFunction; + /** + * Constructs a {@link Compatible} instance with the specified version and conditional function. + * + *

    This constructor initializes a {@link Compatible} object with a given version and a function + * that defines the logic to execute when a version condition is satisfied. The conditional function + * is applied only if the version meets the criteria defined in subsequent {@link #on(String, String, Function)} + * or {@link #on(Operator, Version, Function)} calls.

    + * + *

    Example Usage

    + *
    {@code
    +     * Version currentVersion = Version.of("1.2.3");
    +     * Compatible compatible = new Compatible<>(currentVersion, v -> "Default Result");
    +     *
    +     * // Execute logic only if current version is greater than "1.2.0"
    +     * Optional result = compatible.on(">", "1.2.0", v -> "Condition Matched").call();
    +     * System.out.println(result.orElse("No Match")); // Output: Condition Matched
    +     * }
    + * + * @param version the version to be used for comparison + * @param conditionalFunction the function to execute if a condition is satisfied, or {@code null} if no default action is needed + */ public Compatible(Version version, Function conditionalFunction) { this.version = version; this.conditionalFunction = conditionalFunction; } + /** + * Creates a {@link Compatible} instance for the specified class, automatically detecting its version. + * + *

    This factory method retrieves the version associated with the given class (typically from its JAR manifest) + * and initializes a {@link Compatible} instance with no initial conditional function. This is useful for + * performing version-based compatibility checks on classes without manually specifying their versions.

    + * + *

    Example Usage

    + *
    {@code
    +     * // Automatically fetches version from the manifest of the JAR containing MyComponent.class
    +     * Compatible compatibilityCheck = Compatible.of(MyComponent.class);
    +     *
    +     * boolean isSupported = compatibilityCheck
    +     *     .on(">=", "1.5.0", v -> true)
    +     *     .on("<", "1.5.0", v -> false)
    +     *     .get() != null;
    +     *
    +     * System.out.println("Feature supported: " + isSupported); // Output: Feature supported: true (if version >= 1.5.0)
    +     * }
    + * + * @param targetClass the class for which to create a {@link Compatible} instance + * @param the type of the target class + * @return a new {@link Compatible} instance initialized with the detected version of the target class + * @see Version#getVersion(Class) + */ public static Compatible of(Class targetClass) { - return new Compatible<>(Version.getVersion(targetClass), null); + return new Compatible<>(getVersion(targetClass), null); } + /** + * Adds a conditional function to be executed if the current version satisfies the specified operator and compared version. + * + *

    This method allows chaining multiple conditions. If the current version meets the condition defined by the + * operator and compared version, the provided function will be executed. Otherwise, the chain continues to the next condition.

    + * + *

    Example Usage

    + *
    {@code
    +     * Version currentVersion = Version.of("1.2.3");
    +     * Compatible compatible = new Compatible<>(currentVersion, v -> "Default Result");
    +     *
    +     * // Execute logic only if current version is greater than "1.2.0"
    +     * Optional result = compatible.on(">", "1.2.0", v -> "Condition Matched").call();
    +     * System.out.println(result.orElse("No Match")); // Output: Condition Matched
    +     * }
    + * + * @param operator the operator to compare versions (e.g., ">", "<", "==", ">=", "<=") + * @param comparedVersion the version to compare against, as a string + * @param conditionalFunction the function to execute if the condition is met + * @param the return type of the conditional function + * @return a new {@link Compatible} instance with the added condition, or the same instance if the condition is not met + */ public Compatible on(String operator, String comparedVersion, Function conditionalFunction) { - return on(Version.Operator.of(operator), Version.of(comparedVersion), conditionalFunction); + return on(Operator.of(operator), ofVersion(comparedVersion), conditionalFunction); } - public Compatible on(Version.Operator operator, Version comparedVersion, + /** + * Adds a conditional function to be executed if the current version satisfies the specified operator and compared version. + * + *

    This method allows chaining multiple conditions. If the current version meets the condition defined by the + * operator and compared version, the provided function will be executed. Otherwise, the chain continues to the next condition.

    + * + *

    Example Usage

    + *
    {@code
    +     * Version currentVersion = Version.of("1.2.3");
    +     * Compatible compatible = new Compatible<>(currentVersion, v -> "Default Result");
    +     *
    +     * // Execute logic only if current version is greater than "1.2.0"
    +     * Optional result = compatible.on(Operator.GT, Version.of("1.2.0"), v -> "Condition Matched").call();
    +     * System.out.println(result.orElse("No Match")); // Output: Condition Matched
    +     * }
    + * + * @param operator the operator to compare versions (e.g., Operator.GT, Operator.LT, Operator.EQ, Operator.GE, Operator.LE) + * @param comparedVersion the version to compare against + * @param conditionalFunction the function to execute if the condition is met + * @param the return type of the conditional function + * @return a new {@link Compatible} instance with the added condition, or the same instance if the condition is not met + */ + public Compatible on(Operator operator, Version comparedVersion, Function conditionalFunction) { if (TRUE.equals(operator.test(this.version, comparedVersion))) { return new Compatible<>(version, conditionalFunction); @@ -106,7 +199,23 @@ public Compatible on(Version.Operator operator, Version comparedVersio } /** - * @return + * Executes the conditional function if a matching condition was found and returns the result wrapped in an {@link Optional}. + * If no condition matches or no function is defined, returns an empty {@link Optional}. + * + *

    This method is typically called after chaining one or more {@link #on(String, String, Function)} conditions. + * It evaluates the provided function (if any) against the current version and returns the computed result.

    + * + *

    Example Usage

    + *
    {@code
    +     * Version currentVersion = Version.of("1.2.3");
    +     * Compatible compatible = new Compatible<>(currentVersion, v -> "Default Result");
    +     *
    +     * // Execute logic only if current version is greater than "1.2.0"
    +     * Optional result = compatible.on(">", "1.2.0", v -> "Condition Matched").call();
    +     * System.out.println(result.orElse("No Match")); // Output: Condition Matched
    +     * }
    + * + * @return an {@link Optional} containing the result of the conditional function, or empty if no condition matched */ public Optional call() { R result = null; @@ -116,15 +225,51 @@ public Optional call() { return ofNullable(result); } + /** + * Accepts a consumer to process the result of the conditional function if a matching condition was found. + * This method is useful for side-effect operations such as logging or updating state based on the result. + * + *

    If a condition matches and the conditional function produces a result, the provided {@link Consumer} + * will be invoked with that result. If no condition matches or no function is defined, the consumer is not called.

    + * + *

    Example Usage

    + *
    {@code
    +     * Version currentVersion = Version.of("1.2.3");
    +     * Compatible compatible = new Compatible<>(currentVersion, v -> "Default Result");
    +     *
    +     * // Process the result if the condition matches
    +     * compatible.on(">", "1.2.0", v -> "Condition Matched")
    +     *           .accept(result -> System.out.println("Result: " + result)); // Output: Result: Condition Matched
    +     * }
    + * + * @param resultConsumer the consumer to accept the result of the conditional function + */ public void accept(Consumer resultConsumer) { call().ifPresent(resultConsumer); } /** - * @return + * Retrieves the result of the conditional function if a matching condition was found. + * If no condition matches or no function is defined, returns {@code null}. + * + *

    This method is a convenience wrapper around {@link #call()} that directly returns + * the result instead of wrapping it in an {@link Optional}. It is useful when you are + * certain that a condition will match and want to avoid handling {@link Optional}.

    + * + *

    Example Usage

    + *
    {@code
    +     * Version currentVersion = Version.of("1.2.3");
    +     * Compatible compatible = new Compatible<>(currentVersion, v -> "Default Result");
    +     *
    +     * // Get the result directly if the condition matches
    +     * String result = compatible.on(">", "1.2.0", v -> "Condition Matched").get();
    +     * System.out.println(result); // Output: Condition Matched
    +     * }
    + * + * @return the result of the conditional function, or {@code null} if no condition matched */ @Nullable public R get() { return call().orElse(null); } -} +} \ No newline at end of file From 3d83f5e8e36049bb52dcf7c686f9e3a58ec04add Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 09:58:27 +0800 Subject: [PATCH 189/438] Add null-check for Compatible.call() in test Add a static import for assertNotNull and extend testAccept() to create a Compatible via of(Test.class) and assert that compatible.call() is not null. This ensures the Compatible.call() result is validated in the unit test. --- .../src/test/java/io/microsphere/util/CompatibleTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/CompatibleTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/CompatibleTest.java index 4b00a8fd7..4a1753d8b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/CompatibleTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/CompatibleTest.java @@ -20,6 +20,7 @@ import static io.microsphere.util.Compatible.of; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * {@link Compatible} Test @@ -43,5 +44,8 @@ void testGet() { @Test void testAccept() { compatible.accept(r -> assertEquals(">=", r)); + + Compatible compatible = of(Test.class); + assertNotNull(compatible.call()); } } From 23ae43234c8dc68037c13d2b95bd652257e5eb31 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 13:05:32 +0800 Subject: [PATCH 190/438] Add MBeanFeatureInfoBuilder class Introduce MBeanFeatureInfoBuilder to provide a fluent builder for javax.management.MBeanFeatureInfo. The class exposes protected fields for name, description and descriptor and fluent setter methods returning a generic builder type (B extends MBeanFeatureInfoBuilder). Includes ASF license header and author metadata. Intended as a base builder for MBean feature info construction. --- .../builder/MBeanFeatureInfoBuilder.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java new file mode 100644 index 000000000..a0a628d8f --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + +import javax.management.Descriptor; +import javax.management.MBeanFeatureInfo; + +/** + * {@link MBeanFeatureInfo} Builder + * + * @author Mercy + * @see MBeanFeatureInfo + * @since 1.0.0 + */ +public class MBeanFeatureInfoBuilder { + + /** + * The name of the feature. + * + * @serial The name of the feature. + */ + protected String name; + + /** + * The human-readable description of the feature. + * + * @serial The human-readable description of the feature. + */ + protected String description; + + /** + * @serial The Descriptor for this MBeanFeatureInfo. This field + * can be null, which is equivalent to an empty Descriptor. + */ + protected Descriptor descriptor; + + MBeanFeatureInfoBuilder() { + } + + public B name(String name) { + this.name = name; + return (B) this; + } + + public B description(String description) { + this.description = description; + return (B) this; + } + + public B descriptor(Descriptor descriptor) { + this.descriptor = descriptor; + return (B) this; + } +} \ No newline at end of file From 92d7075423bb4a88339620c1a531c9bf75c3f1b5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 13:05:46 +0800 Subject: [PATCH 191/438] Add MBeanFeatureInfoBuilder unit tests Introduce MBeanFeatureInfoBuilderTest with JUnit 5 tests that verify setting name, description, and descriptor on MBeanFeatureInfoBuilder. The test class initializes a builder in @BeforeEach and asserts the internal fields after calling the corresponding setter methods. --- .../builder/MBeanFeatureInfoBuilderTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java new file mode 100644 index 000000000..3248d6d56 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java @@ -0,0 +1,62 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.management.modelmbean.DescriptorSupport; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link MBeanFeatureInfoBuilder} Test + * + * @author Mercy + * @see MBeanFeatureInfoBuilder + * @since 1.0.0 + */ +class MBeanFeatureInfoBuilderTest { + + private MBeanFeatureInfoBuilder builder; + + @BeforeEach + void setUp() { + this.builder = new MBeanFeatureInfoBuilder(); + } + + @Test + void testName() { + this.builder.name("test"); + assertEquals("test", this.builder.name); + } + + @Test + void testDescription() { + this.builder.description("test"); + assertEquals("test", this.builder.description); + } + + @Test + void testDescriptor() { + DescriptorSupport descriptor = new DescriptorSupport(); + this.builder.descriptor(descriptor); + assertEquals(descriptor, this.builder.descriptor); + } +} \ No newline at end of file From 73412207690619938aebe6e9cc488a879e6726f8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 13:11:22 +0800 Subject: [PATCH 192/438] Assert builder methods return same instance in tests Add assertions to MBeanFeatureInfoBuilderTest to verify that name, description, and descriptor methods return the same builder instance (supporting fluent chaining). Also add the static import for assertSame. --- .../management/builder/MBeanFeatureInfoBuilderTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java index 3248d6d56..d21397e9d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java @@ -24,6 +24,7 @@ import javax.management.modelmbean.DescriptorSupport; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link MBeanFeatureInfoBuilder} Test @@ -43,20 +44,20 @@ void setUp() { @Test void testName() { - this.builder.name("test"); + assertSame(this.builder, this.builder.name("test")); assertEquals("test", this.builder.name); } @Test void testDescription() { - this.builder.description("test"); + assertSame(this.builder, this.builder.description("test")); assertEquals("test", this.builder.description); } @Test void testDescriptor() { DescriptorSupport descriptor = new DescriptorSupport(); - this.builder.descriptor(descriptor); + assertSame(this.builder, this.builder.descriptor(descriptor)); assertEquals(descriptor, this.builder.descriptor); } } \ No newline at end of file From c355842d3f197394d81939c56bfbb254f7c081a0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 13:18:30 +0800 Subject: [PATCH 193/438] Add MBeanAttributeInfoBuilder Introduce MBeanAttributeInfoBuilder in io.microsphere.management.builder to construct javax.management.MBeanAttributeInfo instances. The builder captures attributeType, read/write/is flags, name, description and descriptor, provides fluent setters and a build() method, plus static attribute(Class)/attribute(String) factory methods. Validates non-null attributeType and uses ClassUtils.getTypeName for Class overload. --- .../builder/MBeanAttributeInfoBuilder.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java new file mode 100644 index 000000000..1e8da01a8 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java @@ -0,0 +1,90 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + +import io.microsphere.annotation.Nonnull; + +import javax.management.MBeanAttributeInfo; + +import static io.microsphere.util.Assert.assertNotNull; +import static io.microsphere.util.ClassUtils.getTypeName; + +/** + * {@link MBeanAttributeInfo} Builder + * + * @author Mercy + * @see MBeanAttributeInfo + * @since 1.0.0 + */ +public class MBeanAttributeInfoBuilder extends MBeanFeatureInfoBuilder { + + /** + * @serial The actual attribute type. + */ + private @Nonnull String attributeType; + + /** + * @serial The attribute write right. + */ + private boolean write; + + /** + * @serial The attribute read right. + */ + private boolean read; + + /** + * @serial Indicates if this method is a "is" + */ + private boolean is; + + MBeanAttributeInfoBuilder() { + super(); + } + + public MBeanAttributeInfoBuilder write(boolean isWritable) { + this.write = isWritable; + return this; + } + + public MBeanAttributeInfoBuilder read(boolean isReadable) { + this.read = isReadable; + return this; + } + + public MBeanAttributeInfoBuilder is(boolean isIs) { + this.is = isIs; + return this; + } + + public MBeanAttributeInfo build() { + return new MBeanAttributeInfo(this.name, this.attributeType, this.description, this.read, this.write, + this.is, this.descriptor); + } + + public static MBeanAttributeInfoBuilder attribute(@Nonnull Class attributeType) { + return attribute(getTypeName(attributeType)); + } + + public static MBeanAttributeInfoBuilder attribute(@Nonnull String attributeType) { + assertNotNull(attributeType, () -> "The 'attributeType' must not be null"); + MBeanAttributeInfoBuilder builder = new MBeanAttributeInfoBuilder(); + builder.attributeType = attributeType; + return builder; + } +} \ No newline at end of file From c5a33df79238cb92d432e2bdc25db4ea331134f0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 13:18:38 +0800 Subject: [PATCH 194/438] Add MBeanAttributeInfoBuilder unit test Introduce MBeanAttributeInfoBuilderTest which verifies the builder's fluent API and the built MBeanAttributeInfo. The test asserts that setter methods return the same builder instance and that name, description, descriptor, type, and readable/writable/is flags are set correctly. --- .../MBeanAttributeInfoBuilderTest.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java new file mode 100644 index 000000000..c70841d7a --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java @@ -0,0 +1,70 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.management.MBeanAttributeInfo; +import javax.management.modelmbean.DescriptorSupport; + +import static io.microsphere.management.builder.MBeanAttributeInfoBuilder.attribute; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link MBeanAttributeInfoBuilder} Test + * + * @author Mercy + * @see MBeanAttributeInfoBuilder + * @since 1.0.0 + */ +class MBeanAttributeInfoBuilderTest { + + private MBeanAttributeInfoBuilder builder; + + @BeforeEach + void setUp() { + this.builder = attribute(Boolean.class); + } + + @Test + void testBuild() { + String name = "test"; + String descrption = "NoDesc"; + DescriptorSupport descriptor = new DescriptorSupport(); + + assertSame(this.builder, this.builder.name(name)); + assertSame(this.builder, this.builder.description(descrption)); + assertSame(this.builder, this.builder.descriptor(descriptor)); + assertSame(this.builder, this.builder.read(true)); + assertSame(this.builder, this.builder.write(true)); + assertSame(this.builder, this.builder.is(true)); + + MBeanAttributeInfo info = this.builder.build(); + assertEquals(name, info.getName()); + assertEquals(descrption, info.getDescription()); + assertEquals(descriptor, info.getDescriptor()); + assertEquals(Boolean.class.getName(), info.getType()); + assertTrue(info.isReadable()); + assertTrue(info.isWritable()); + assertTrue(info.isIs()); + } +} \ No newline at end of file From f141ca800b6d26fce341245b1cd77e25e38db4b2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 16:34:37 +0800 Subject: [PATCH 195/438] Add build() to MBeanFeatureInfoBuilder Add a build() method to MBeanFeatureInfoBuilder that constructs and returns an MBeanFeatureInfo using the builder's name, description, and descriptor fields. Provides a convenient terminal step for the builder pattern to create the immutable MBeanFeatureInfo instance. --- .../management/builder/MBeanFeatureInfoBuilder.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java index a0a628d8f..e899cf655 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java @@ -66,4 +66,8 @@ public B descriptor(Descriptor descriptor) { this.descriptor = descriptor; return (B) this; } + + public MBeanFeatureInfo build() { + return new MBeanFeatureInfo(this.name, this.description, this.descriptor); + } } \ No newline at end of file From cb956eeff9d944216a7258b8fdad427729467cfc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 16:34:58 +0800 Subject: [PATCH 196/438] Add build tests to MBeanFeatureInfoBuilderTest Refactor the test class to use reusable constants and a generic builder factory, and add comprehensive build behavior tests. Introduces TEST_NAME, TEST_DESCRIPTION and TEST_DESCRIPTOR constants, a protected builder() method to allow subclassing, and imports EMPTY_DESCRIPTOR and assertNull. Replaces literal strings in existing tests and adds testBuild to verify default values (null name/description and EMPTY_DESCRIPTOR) and that name, description and descriptor are applied after calling the builder setters. --- .../builder/MBeanFeatureInfoBuilderTest.java | 51 +++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java index d21397e9d..2de520526 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java @@ -21,9 +21,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import javax.management.MBeanFeatureInfo; import javax.management.modelmbean.DescriptorSupport; +import static javax.management.ImmutableDescriptor.EMPTY_DESCRIPTOR; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; /** @@ -33,31 +36,61 @@ * @see MBeanFeatureInfoBuilder * @since 1.0.0 */ -class MBeanFeatureInfoBuilderTest { +class MBeanFeatureInfoBuilderTest { + + static final String TEST_NAME = "test-name"; + + static final String TEST_DESCRIPTION = "test-desc"; + + static final DescriptorSupport TEST_DESCRIPTOR = new DescriptorSupport(); + private MBeanFeatureInfoBuilder builder; @BeforeEach void setUp() { - this.builder = new MBeanFeatureInfoBuilder(); + this.builder = builder(); + } + + protected B builder() { + return (B) new MBeanFeatureInfoBuilder(); } @Test void testName() { - assertSame(this.builder, this.builder.name("test")); - assertEquals("test", this.builder.name); + assertSame(this.builder, this.builder.name(TEST_NAME)); + assertEquals(TEST_NAME, this.builder.name); } @Test void testDescription() { - assertSame(this.builder, this.builder.description("test")); - assertEquals("test", this.builder.description); + assertSame(this.builder, this.builder.description(TEST_DESCRIPTION)); + assertEquals(TEST_DESCRIPTION, this.builder.description); } @Test void testDescriptor() { - DescriptorSupport descriptor = new DescriptorSupport(); - assertSame(this.builder, this.builder.descriptor(descriptor)); - assertEquals(descriptor, this.builder.descriptor); + assertSame(this.builder, this.builder.descriptor(TEST_DESCRIPTOR)); + assertEquals(TEST_DESCRIPTOR, this.builder.descriptor); + } + + @Test + void testBuild() { + MBeanFeatureInfo info = this.builder.build(); + assertNull(info.getName()); + assertNull(info.getDescription()); + assertSame(EMPTY_DESCRIPTOR, info.getDescriptor()); + + assertSame(this.builder, this.builder.name(TEST_NAME)); + info = this.builder.build(); + assertSame(TEST_NAME, info.getName()); + + assertSame(this.builder, this.builder.description(TEST_DESCRIPTION)); + info = this.builder.build(); + assertEquals(TEST_DESCRIPTION, info.getDescription()); + + assertSame(this.builder, this.builder.descriptor(TEST_DESCRIPTOR)); + info = this.builder.build(); + assertEquals(TEST_DESCRIPTOR, info.getDescriptor()); } } \ No newline at end of file From 85ae23db85434b8a2080b335813250e5cdb57b5f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 16:48:04 +0800 Subject: [PATCH 197/438] Add AbstractMBeanFeatureInfoBuilderTest Introduce a generic unit test for MBeanFeatureInfoBuilder implementations. The test verifies name, description and descriptor setters return the builder, and that build() produces an MBeanFeatureInfo with correct default (null name/description, EMPTY_DESCRIPTOR) and updated values after setting them. Uses reflection to instantiate the concrete builder type and covers DescriptorSupport usage. --- .../AbstractMBeanFeatureInfoBuilderTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java new file mode 100644 index 000000000..a8ba061e8 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.management.MBeanFeatureInfo; +import javax.management.modelmbean.DescriptorSupport; + +import static io.microsphere.reflect.JavaType.from; +import static io.microsphere.util.ClassUtils.newInstance; +import static javax.management.ImmutableDescriptor.EMPTY_DESCRIPTOR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * Abstract test class for {@link MBeanFeatureInfoBuilder} + * + * @author Mercy + * @see MBeanFeatureInfoBuilderTest + * @since 1.0.0 + */ +public class AbstractMBeanFeatureInfoBuilderTest { + + static final String TEST_NAME = "test-name"; + + static final String TEST_DESCRIPTION = "test-desc"; + + static final DescriptorSupport TEST_DESCRIPTOR = new DescriptorSupport(); + + protected B builder; + + @BeforeEach + void setUp() { + this.builder = builder(); + } + + protected B builder() { + Class builderClass = from(getClass()) + .as(AbstractMBeanFeatureInfoBuilderTest.class) + .getGenericType(0) + .toClass(); + return newInstance(builderClass); + } + + @Test + void testName() { + assertSame(this.builder, this.builder.name(TEST_NAME)); + assertEquals(TEST_NAME, this.builder.name); + } + + @Test + void testDescription() { + assertSame(this.builder, this.builder.description(TEST_DESCRIPTION)); + assertEquals(TEST_DESCRIPTION, this.builder.description); + } + + @Test + void testDescriptor() { + assertSame(this.builder, this.builder.descriptor(TEST_DESCRIPTOR)); + assertEquals(TEST_DESCRIPTOR, this.builder.descriptor); + } + + @Test + void testBuild() { + MBeanFeatureInfo info = this.builder.build(); + assertNull(info.getName()); + assertNull(info.getDescription()); + assertSame(EMPTY_DESCRIPTOR, info.getDescriptor()); + + assertSame(this.builder, this.builder.name(TEST_NAME)); + info = this.builder.build(); + assertSame(TEST_NAME, info.getName()); + + assertSame(this.builder, this.builder.description(TEST_DESCRIPTION)); + info = this.builder.build(); + assertEquals(TEST_DESCRIPTION, info.getDescription()); + + assertSame(this.builder, this.builder.descriptor(TEST_DESCRIPTOR)); + info = this.builder.build(); + assertEquals(TEST_DESCRIPTOR, info.getDescriptor()); + } +} From 123ce746008fec9698cfcb943e953c25b90f4543 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 16:48:29 +0800 Subject: [PATCH 198/438] Use AbstractMBeanFeatureInfoBuilderTest in test Replace the concrete MBeanFeatureInfoBuilderTest implementation with a lightweight subclass of AbstractMBeanFeatureInfoBuilderTest. Removes duplicated test helpers, constants, setup and individual test methods in favor of shared test logic provided by the abstract base to improve reuse and reduce duplication. --- .../builder/MBeanFeatureInfoBuilderTest.java | 69 +------------------ 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java index 2de520526..34eefb29a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanFeatureInfoBuilderTest.java @@ -18,17 +18,6 @@ package io.microsphere.management.builder; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.management.MBeanFeatureInfo; -import javax.management.modelmbean.DescriptorSupport; - -import static javax.management.ImmutableDescriptor.EMPTY_DESCRIPTOR; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - /** * {@link MBeanFeatureInfoBuilder} Test * @@ -36,61 +25,5 @@ * @see MBeanFeatureInfoBuilder * @since 1.0.0 */ -class MBeanFeatureInfoBuilderTest { - - static final String TEST_NAME = "test-name"; - - static final String TEST_DESCRIPTION = "test-desc"; - - static final DescriptorSupport TEST_DESCRIPTOR = new DescriptorSupport(); - - - private MBeanFeatureInfoBuilder builder; - - @BeforeEach - void setUp() { - this.builder = builder(); - } - - protected B builder() { - return (B) new MBeanFeatureInfoBuilder(); - } - - @Test - void testName() { - assertSame(this.builder, this.builder.name(TEST_NAME)); - assertEquals(TEST_NAME, this.builder.name); - } - - @Test - void testDescription() { - assertSame(this.builder, this.builder.description(TEST_DESCRIPTION)); - assertEquals(TEST_DESCRIPTION, this.builder.description); - } - - @Test - void testDescriptor() { - assertSame(this.builder, this.builder.descriptor(TEST_DESCRIPTOR)); - assertEquals(TEST_DESCRIPTOR, this.builder.descriptor); - } - - @Test - void testBuild() { - MBeanFeatureInfo info = this.builder.build(); - assertNull(info.getName()); - assertNull(info.getDescription()); - assertSame(EMPTY_DESCRIPTOR, info.getDescriptor()); - - assertSame(this.builder, this.builder.name(TEST_NAME)); - info = this.builder.build(); - assertSame(TEST_NAME, info.getName()); - - assertSame(this.builder, this.builder.description(TEST_DESCRIPTION)); - info = this.builder.build(); - assertEquals(TEST_DESCRIPTION, info.getDescription()); - - assertSame(this.builder, this.builder.descriptor(TEST_DESCRIPTOR)); - info = this.builder.build(); - assertEquals(TEST_DESCRIPTOR, info.getDescriptor()); - } +class MBeanFeatureInfoBuilderTest extends AbstractMBeanFeatureInfoBuilderTest { } \ No newline at end of file From d4c34665c8e50253dc231c64b86d1034ba8a0ae6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 16:51:05 +0800 Subject: [PATCH 199/438] Add attributeType setter and refactor factory Introduce a fluent attributeType(String) setter on MBeanAttributeInfoBuilder and update the static attribute(String) factory to delegate to it. Removes redundant direct assignment in the factory and improves builder API consistency without changing runtime behavior. --- .../management/builder/MBeanAttributeInfoBuilder.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java index 1e8da01a8..5b94baca2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java @@ -57,6 +57,11 @@ public class MBeanAttributeInfoBuilder extends MBeanFeatureInfoBuilder attributeTyp public static MBeanAttributeInfoBuilder attribute(@Nonnull String attributeType) { assertNotNull(attributeType, () -> "The 'attributeType' must not be null"); - MBeanAttributeInfoBuilder builder = new MBeanAttributeInfoBuilder(); - builder.attributeType = attributeType; - return builder; + return new MBeanAttributeInfoBuilder().attributeType(attributeType); } } \ No newline at end of file From 6c9f2e3d1a1348d16807ad420bbfb6a7628f7981 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 16:51:21 +0800 Subject: [PATCH 200/438] Use base test class for MBeanAttributeInfoBuilder Refactor test to extend AbstractMBeanFeatureInfoBuilderTest, removing the local builder field and @BeforeEach setup. Drop the now-unused static import and BeforeEach import. Add an assertion for attributeType("java.lang.Boolean") to verify fluent API chaining. --- .../builder/MBeanAttributeInfoBuilderTest.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java index c70841d7a..dd6a51964 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java @@ -18,13 +18,11 @@ package io.microsphere.management.builder; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import javax.management.MBeanAttributeInfo; import javax.management.modelmbean.DescriptorSupport; -import static io.microsphere.management.builder.MBeanAttributeInfoBuilder.attribute; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -36,14 +34,7 @@ * @see MBeanAttributeInfoBuilder * @since 1.0.0 */ -class MBeanAttributeInfoBuilderTest { - - private MBeanAttributeInfoBuilder builder; - - @BeforeEach - void setUp() { - this.builder = attribute(Boolean.class); - } +class MBeanAttributeInfoBuilderTest extends AbstractMBeanFeatureInfoBuilderTest { @Test void testBuild() { @@ -51,6 +42,7 @@ void testBuild() { String descrption = "NoDesc"; DescriptorSupport descriptor = new DescriptorSupport(); + assertSame(this.builder, this.builder.attributeType("java.lang.Boolean")); assertSame(this.builder, this.builder.name(name)); assertSame(this.builder, this.builder.description(descrption)); assertSame(this.builder, this.builder.descriptor(descriptor)); From 29614011bbf8b5037bedbfb291b5d6eedf2da0e8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 16:57:07 +0800 Subject: [PATCH 201/438] Set attributeType directly in factory Remove the fluent attributeType(...) setter and assign attributeType directly in the static attribute(...) factory. The @Nonnull annotation was reformatted to its own line. The factory still asserts non-null for attributeType, but the field is now set internally on builder creation, simplifying the builder API and preventing external mutation via the removed setter. --- .../builder/MBeanAttributeInfoBuilder.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java index 5b94baca2..437f07ac1 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java @@ -36,7 +36,8 @@ public class MBeanAttributeInfoBuilder extends MBeanFeatureInfoBuilder attributeTyp public static MBeanAttributeInfoBuilder attribute(@Nonnull String attributeType) { assertNotNull(attributeType, () -> "The 'attributeType' must not be null"); - return new MBeanAttributeInfoBuilder().attributeType(attributeType); + MBeanAttributeInfoBuilder builder = new MBeanAttributeInfoBuilder(); + builder.attributeType = attributeType; + return builder; } } \ No newline at end of file From 215c9443cc0d6059de6124677774f50eeaa19efe Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 16:57:17 +0800 Subject: [PATCH 202/438] Use attribute(Boolean.class) in test builder Add static import for MBeanAttributeInfoBuilder.attribute and override builder() to return attribute(Boolean.class) for test setup. Remove redundant assertion that previously set attributeType("java.lang.Boolean"). This simplifies initialization and centralizes builder creation in the test. --- .../management/builder/MBeanAttributeInfoBuilderTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java index dd6a51964..24aa84965 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanAttributeInfoBuilderTest.java @@ -23,6 +23,7 @@ import javax.management.MBeanAttributeInfo; import javax.management.modelmbean.DescriptorSupport; +import static io.microsphere.management.builder.MBeanAttributeInfoBuilder.attribute; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -36,13 +37,17 @@ */ class MBeanAttributeInfoBuilderTest extends AbstractMBeanFeatureInfoBuilderTest { + @Override + protected MBeanAttributeInfoBuilder builder() { + return attribute(Boolean.class); + } + @Test void testBuild() { String name = "test"; String descrption = "NoDesc"; DescriptorSupport descriptor = new DescriptorSupport(); - assertSame(this.builder, this.builder.attributeType("java.lang.Boolean")); assertSame(this.builder, this.builder.name(name)); assertSame(this.builder, this.builder.description(descrption)); assertSame(this.builder, this.builder.descriptor(descriptor)); From 6fcf1e12bb741becd827b2473590ee54fccd6697 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 17:05:18 +0800 Subject: [PATCH 203/438] Add MBeanParameterInfoBuilder Introduce MBeanParameterInfoBuilder in io.microsphere.management.builder to construct javax.management.MBeanParameterInfo instances. The builder extends MBeanFeatureInfoBuilder, holds a 'type' field, provides build(), and static factory methods parameter(Class) (uses getTypeName) and parameter(String) for convenient creation. --- .../builder/MBeanParameterInfoBuilder.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java new file mode 100644 index 000000000..6a4487f7f --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java @@ -0,0 +1,61 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + +import io.microsphere.annotation.Nonnull; + +import javax.management.MBeanFeatureInfo; +import javax.management.MBeanParameterInfo; + +import static io.microsphere.util.ClassUtils.getTypeName; + +/** + * {@link MBeanParameterInfo} Builder + * + * @author Mercy + * @see MBeanParameterInfo + * @see MBeanFeatureInfo + * @see MBeanFeatureInfoBuilder + * @since 1.0.0 + */ +public class MBeanParameterInfoBuilder extends MBeanFeatureInfoBuilder { + + /** + * The type or class name of the data. + */ + @Nonnull + private String type; + + MBeanParameterInfoBuilder() { + super(); + } + + public MBeanParameterInfo build() { + return new MBeanParameterInfo(this.name, this.type, this.description, this.descriptor); + } + + public static MBeanParameterInfoBuilder parameter(@Nonnull Class type) { + return parameter(getTypeName(type)); + } + + public static MBeanParameterInfoBuilder parameter(@Nonnull String type) { + MBeanParameterInfoBuilder builder = new MBeanParameterInfoBuilder(); + builder.type = type; + return builder; + } +} From 78fee9820ee9497ba65d0c3831c1e9250e3510d5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 17:05:27 +0800 Subject: [PATCH 204/438] Add MBeanParameterInfoBuilder unit test Add MBeanParameterInfoBuilderTest to verify that MBeanParameterInfoBuilder.parameter(String.class) constructs an MBeanParameterInfo with the correct type (java.lang.String). The test extends AbstractMBeanFeatureInfoBuilderTest and implements builder() and testBuild() methods. --- .../MBeanParameterInfoBuilderTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanParameterInfoBuilderTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanParameterInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanParameterInfoBuilderTest.java new file mode 100644 index 000000000..0d95c2b37 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanParameterInfoBuilderTest.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + + +import javax.management.MBeanParameterInfo; + +import static io.microsphere.management.builder.MBeanParameterInfoBuilder.parameter; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link MBeanParameterInfoBuilder} Test + * + * @author Mercy + * @see MBeanParameterInfoBuilder + * @since 1.0.0 + */ +class MBeanParameterInfoBuilderTest extends AbstractMBeanFeatureInfoBuilderTest { + + @Override + protected MBeanParameterInfoBuilder builder() { + return parameter(String.class); + } + + @Override + void testBuild() { + MBeanParameterInfo info = this.builder.build(); + assertEquals(String.class.getName(), info.getType()); + } +} \ No newline at end of file From ede078de92bf107a9910f011c59fb87bb94e1ab2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 17:05:45 +0800 Subject: [PATCH 205/438] Disable AbstractMBeanFeatureInfoBuilderTest Add org.junit.jupiter.api.Disabled import and annotate the test class with @Disabled to prevent JUnit from running this base test. Also change the class from public to package-private and declare it abstract so it serves only as a superclass for concrete test implementations. --- .../builder/AbstractMBeanFeatureInfoBuilderTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java index a8ba061e8..3b564925e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java @@ -18,6 +18,7 @@ package io.microsphere.management.builder; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import javax.management.MBeanFeatureInfo; @@ -37,7 +38,8 @@ * @see MBeanFeatureInfoBuilderTest * @since 1.0.0 */ -public class AbstractMBeanFeatureInfoBuilderTest { +@Disabled +abstract class AbstractMBeanFeatureInfoBuilderTest { static final String TEST_NAME = "test-name"; From 5df844cf88ac5aad420892f8fb3e6a15fc21cfec Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 19:59:28 +0800 Subject: [PATCH 206/438] Add @Units descriptor annotation Add a test-only annotation `@Units` under io.microsphere.management. The annotation is targeted at methods, retained at runtime, and meta-annotated with `@DescriptorKey("units")` to attach a JMX descriptor key for units. This supports management-related tests that need unit metadata on MBean attributes/operations. --- .../java/io/microsphere/management/Units.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/Units.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/Units.java b/microsphere-java-core/src/test/java/io/microsphere/management/Units.java new file mode 100644 index 000000000..a4a4abba9 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/Units.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package io.microsphere.management; + +import javax.management.DescriptorKey; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * The annotation annotated by {@link DescriptorKey @DescriptorKey} + * + * @author Mercy + * @see DescriptorKey + * @since 1.0.0 + */ +@Documented +@Target(METHOD) +@Retention(RUNTIME) +public @interface Units { + + @DescriptorKey("units") + String value(); +} From 89ab6d394e280bd34d0e425cd6ee7044def8b21f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 19:59:40 +0800 Subject: [PATCH 207/438] Add CacheControlMBean test interface Introduce a new test interface CacheControlMBean in src/test/java/io/microsphere/management. The file includes an ASF license header and Javadoc, and defines a single method getCacheSize() annotated with @Units("bytes") as an example for the Units annotation. --- .../management/CacheControlMBean.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/CacheControlMBean.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/CacheControlMBean.java b/microsphere-java-core/src/test/java/io/microsphere/management/CacheControlMBean.java new file mode 100644 index 000000000..5aaaa7d5a --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/CacheControlMBean.java @@ -0,0 +1,31 @@ +/* + * 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. + */ + +package io.microsphere.management; + +/** + * {@link Units} example class + * + * @author Mercy + * @see Units + * @since 1.0.0 + */ +public interface CacheControlMBean { + + @Units("bytes") + long getCacheSize(); +} \ No newline at end of file From fef1aa5b5b020f1b32188147974a316f6b924a17 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 20:00:02 +0800 Subject: [PATCH 208/438] Add JMX Descriptor helpers & improve attributes Introduce descriptorForElement and descriptorForAnnotations to build ImmutableDescriptor from annotations (honoring @DescriptorKey) and return EMPTY_DESCRIPTOR when no entries. Refactor doGetAttribute to handle null/non-readable attributes early, wrap unexpected throwables with ExceptionUtils.wrap, and improve logging (include MBeanInfo in attribute-not-found logs and remove redundant isWarnEnabled checks). Also add related imports and small utility usage (newFixedHashMap, invokeMethod) to support the changes. --- .../io/microsphere/management/JmxUtils.java | 89 +++++++++++++------ 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java index 9b97b0e43..39aa1d141 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java @@ -23,6 +23,9 @@ import io.microsphere.util.Utils; import javax.management.AttributeNotFoundException; +import javax.management.Descriptor; +import javax.management.DescriptorKey; +import javax.management.ImmutableDescriptor; import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; @@ -30,6 +33,7 @@ import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.ReflectionException; +import java.lang.annotation.Annotation; import java.lang.management.ClassLoadingMXBean; import java.lang.management.CompilationMXBean; import java.lang.management.GarbageCollectorMXBean; @@ -40,18 +44,25 @@ import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; import java.lang.management.ThreadMXBean; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import static io.microsphere.collection.MapUtils.newFixedHashMap; import static io.microsphere.collection.MapUtils.newHashMap; import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.reflect.MethodUtils.invokeMethod; import static io.microsphere.util.ArrayUtils.arrayToString; +import static io.microsphere.util.ArrayUtils.length; +import static io.microsphere.util.ExceptionUtils.wrap; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; import static java.util.Optional.ofNullable; +import static javax.management.ImmutableDescriptor.EMPTY_DESCRIPTOR; /** * The utilities class for JMX operations, providing convenient methods to interact with MBeans and MXBeans. @@ -540,55 +551,81 @@ public static MBeanInfo getMBeanInfo(MBeanServer mBeanServer, ObjectName objectN return mBeanInfo; } - protected static Object doGetAttribute(MBeanServer mBeanServer, ObjectName objectName, @Nullable MBeanAttributeInfo attributeInfo, - String attributeName) { + + @Nonnull + public static Descriptor descriptorForElement(AnnotatedElement annotatedElement) { + Annotation[] annotations = annotatedElement.getAnnotations(); + return descriptorForAnnotations(annotations); + } + + @Nonnull + public static Descriptor descriptorForAnnotations(Annotation[] annotations) { + int length = length(annotations); + if (length > 0) { + Map descriptorMap = newFixedHashMap(length); + for (int i = 0; i < length; i++) { + Annotation annotation = annotations[i]; + Class annotationType = annotation.annotationType(); + Method[] methods = annotationType.getMethods(); + for (Method method : methods) { + DescriptorKey key = method.getAnnotation(DescriptorKey.class); + if (key != null) { + String name = key.value(); + Object value = invokeMethod(annotation, method); + descriptorMap.put(name, value); + } + } + } + if (!descriptorMap.isEmpty()) { + return new ImmutableDescriptor(descriptorMap); + } + } + return EMPTY_DESCRIPTOR; + } + + protected static Object doGetAttribute(MBeanServer mBeanServer, ObjectName objectName, + @Nullable MBeanAttributeInfo attributeInfo, String attributeName) { MBeanAttributeInfo mBeanAttributeInfo = attributeInfo; if (mBeanAttributeInfo == null) { mBeanAttributeInfo = findMBeanAttributeInfo(mBeanServer, objectName, attributeName); } - if (mBeanAttributeInfo == null) { - return null; - } - if (!mBeanAttributeInfo.isReadable()) { + if (mBeanAttributeInfo == null || !mBeanAttributeInfo.isReadable()) { return null; } + Object attributeValue = null; try { attributeValue = mBeanServer.getAttribute(objectName, attributeName); } catch (ReflectionException | InstanceNotFoundException e) { handleException(e, mBeanServer, objectName); } catch (Throwable e) { - throw new RuntimeException(e); + throw wrap(e, RuntimeException.class); } return attributeValue; } private static void handleException(Exception e, MBeanServer mBeanServer, ObjectName objectName) { - if (logger.isWarnEnabled()) { - logger.warn("the MBean[name : '{}'] can't be manipulated by the Reflection in the MBeanServer[default domain : '{}' , domains : {}]", - objectName.getCanonicalName(), - mBeanServer.getDefaultDomain(), - arrayToString(mBeanServer.getDomains()), - e - ); - } + logger.warn("the MBean[name : '{}'] can't be manipulated by the Reflection in the MBeanServer[default domain : '{}' , domains : {}]", + objectName.getCanonicalName(), + mBeanServer.getDefaultDomain(), + arrayToString(mBeanServer.getDomains()), + e + ); } private static void handleAttributeNotFoundException(AttributeNotFoundException e, MBeanServer mBeanServer, ObjectName objectName, MBeanInfo mBeanInfo, String attributeName) { - - if (logger.isWarnEnabled()) { - logger.warn("The attribute[name : '{}' ] of MBean[name : '{}'] can't be found in the MBeanServer[default domain : '{}' , domains : {}]", - attributeName, - objectName.getCanonicalName(), - mBeanServer.getDefaultDomain(), - arrayToString(mBeanServer.getDomains()), - e - ); - } + logger.warn("The attribute[name : '{}' ] of MBean[name : '{}' , info : {}] can't be found in the MBeanServer[default domain : '{}' , domains : {}]", + attributeName, + objectName.getCanonicalName(), + mBeanInfo, + mBeanServer.getDefaultDomain(), + arrayToString(mBeanServer.getDomains()), + e + ); } private JmxUtils() { } -} +} \ No newline at end of file From ea601962ae2a310babe7a03ddc3de964430fa18e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 20:00:20 +0800 Subject: [PATCH 209/438] Add JmxUtils tests for descriptors and attributes Introduce new unit tests in JmxUtilsTest to cover descriptor and attribute handling: add testGetAttributeOnUnreadableAttribute which registers a DynamicMBean with an unreadable attribute and asserts null is returned; add placeholder testGetAttributeOnReflectionException; add tests for descriptorForElement (verifies ImmutableDescriptor and field values), descriptorForElementOnNoDisciptorKeyAnnotated (expects EMPTY_DESCRIPTOR), and descriptorForAnnotations null/empty cases. Also add required imports and static imports (findMethod, ofArray, assertArrayEquals, assertInstanceOf, EMPTY_DESCRIPTOR) to support the new tests. --- .../microsphere/management/JmxUtilsTest.java | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java index b2d68e99c..0b4e48698 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java @@ -20,13 +20,28 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.Descriptor; +import javax.management.DynamicMBean; +import javax.management.ImmutableDescriptor; +import javax.management.InvalidAttributeValueException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanInfo; import javax.management.MBeanServer; import javax.management.ObjectName; +import javax.management.ReflectionException; +import java.lang.annotation.Annotation; import java.lang.management.PlatformManagedObject; +import java.lang.reflect.Method; import java.util.Map; import java.util.Optional; import static io.microsphere.management.JmxUtils.EMPTY_MBEAN_ATTRIBUTE_ARRAY; +import static io.microsphere.management.JmxUtils.descriptorForAnnotations; +import static io.microsphere.management.JmxUtils.descriptorForElement; import static io.microsphere.management.JmxUtils.getAttribute; import static io.microsphere.management.JmxUtils.getClassLoadingMXBean; import static io.microsphere.management.JmxUtils.getCompilationMXBean; @@ -39,6 +54,8 @@ import static io.microsphere.management.JmxUtils.getOperatingSystemMXBean; import static io.microsphere.management.JmxUtils.getRuntimeMXBean; import static io.microsphere.management.JmxUtils.getThreadMXBean; +import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.util.ArrayUtils.ofArray; import static java.lang.management.ManagementFactory.CLASS_LOADING_MXBEAN_NAME; import static java.lang.management.ManagementFactory.COMPILATION_MXBEAN_NAME; import static java.lang.management.ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE; @@ -49,8 +66,11 @@ import static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME; import static java.lang.management.ManagementFactory.getPlatformMBeanServer; import static java.util.Collections.emptyMap; +import static javax.management.ImmutableDescriptor.EMPTY_DESCRIPTOR; import static javax.management.ObjectName.getInstance; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; @@ -183,6 +203,85 @@ void testGetAttributeOnAttributeNotFound() { assertNull(value); } + @Test + void testGetAttributeOnUnreadableAttribute() throws Exception { + String attributeName = "a"; + String descrption = "NoDesc"; + MBeanAttributeInfo attribute = new MBeanAttributeInfo(attributeName, "java.lang.String", descrption, false, false, false); + MBeanAttributeInfo[] attributes = ofArray(attribute); + + MBeanInfo mBeanInfo = new MBeanInfo("Test", descrption, attributes, ofArray(), ofArray(), ofArray()); + ObjectName objectName = getInstance("io.microsphere.management:type=Test"); + + DynamicMBean dynamicMBean = new DynamicMBean() { + @Override + public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { + return null; + } + + @Override + public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { + + } + + @Override + public AttributeList getAttributes(String[] attributes) { + return null; + } + + @Override + public AttributeList setAttributes(AttributeList attributes) { + return null; + } + + @Override + public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { + return null; + } + + @Override + public MBeanInfo getMBeanInfo() { + return mBeanInfo; + } + }; + + mBeanServer.registerMBean(dynamicMBean, objectName); + + Object value = getAttribute(mBeanServer, objectName, attributeName); + assertNull(value); + } + + @Test + void testGetAttributeOnReflectionException() { + + } + + @Test + void testDescriptorForElement() { + Method method = findMethod(CacheControlMBean.class, "getCacheSize"); + Descriptor descriptor = descriptorForElement(method); + assertInstanceOf(ImmutableDescriptor.class, descriptor); + ImmutableDescriptor immutableDescriptor = (ImmutableDescriptor) descriptor; + assertArrayEquals(ofArray("units"), immutableDescriptor.getFieldNames()); + assertEquals("bytes", immutableDescriptor.getFieldValue("units")); + } + + @Test + void testDescriptorForElementOnNoDisciptorKeyAnnotated() { + Method method = findMethod(CacheControlMBean.class, "toString"); + Descriptor descriptor = descriptorForElement(method); + assertSame(EMPTY_DESCRIPTOR, descriptor); + } + + @Test + void testDescriptorForAnnotationsOnNull() { + assertSame(EMPTY_DESCRIPTOR, descriptorForAnnotations(null)); + } + + @Test + void testDescriptorForAnnotationsOnEmptyAnnotations() { + assertSame(EMPTY_DESCRIPTOR, descriptorForAnnotations(new Annotation[0])); + } private void assertPlatformMXBean(Optional platformMXBean, String name) throws Throwable { if (platformMXBean.isPresent()) { From 186d2fc0e61ead802a8e1dfecee73a5b2943fe35 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 21:04:03 +0800 Subject: [PATCH 210/438] Add setCacheSize to CacheControlMBean Add a setCacheSize(long) setter (annotated with @Units("bytes")) to the CacheControlMBean test interface so tests can modify the cache size. Also ensure file ends with a newline. --- .../test/java/io/microsphere/management/CacheControlMBean.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/CacheControlMBean.java b/microsphere-java-core/src/test/java/io/microsphere/management/CacheControlMBean.java index 5aaaa7d5a..6e8b7ecb1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/CacheControlMBean.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/CacheControlMBean.java @@ -28,4 +28,6 @@ public interface CacheControlMBean { @Units("bytes") long getCacheSize(); + + void setCacheSize(@Units("bytes") long cacheSize); } \ No newline at end of file From 1aaf73eabfbb4e4c542c17f6dc1b9fe8ea51ea4a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 21:04:25 +0800 Subject: [PATCH 211/438] Add MBeanOperationInfo builder and method signature Introduce MBeanOperationInfoBuilder to construct MBeanOperationInfo (including impact and parameter helpers) and add methodSignature/descriptorForAnnotations helpers in JmxUtils to derive MBeanParameterInfo[] and descriptors from reflection. Add MBeanParameterInfoBuilder.parameter(Parameter) to build parameter metadata from java.lang.reflect.Parameter and extend Units test annotation to target parameters. Include unit tests for methodSignature and MBeanOperationInfoBuilder to validate generated signatures, descriptors and parameter metadata. --- .../io/microsphere/management/JmxUtils.java | 22 +++ .../builder/MBeanOperationInfoBuilder.java | 154 ++++++++++++++++++ .../builder/MBeanParameterInfoBuilder.java | 14 ++ .../microsphere/management/JmxUtilsTest.java | 16 ++ .../java/io/microsphere/management/Units.java | 3 +- .../MBeanOperationInfoBuilderTest.java | 83 ++++++++++ 6 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanOperationInfoBuilderTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java index 39aa1d141..641bbe158 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java @@ -20,6 +20,7 @@ import io.microsphere.annotation.Nonnull; import io.microsphere.annotation.Nullable; import io.microsphere.logging.Logger; +import io.microsphere.management.builder.MBeanParameterInfoBuilder; import io.microsphere.util.Utils; import javax.management.AttributeNotFoundException; @@ -30,6 +31,8 @@ import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.ReflectionException; @@ -46,6 +49,7 @@ import java.lang.management.ThreadMXBean; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.util.List; import java.util.Map; import java.util.Objects; @@ -62,6 +66,7 @@ import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; import static java.util.Optional.ofNullable; +import static java.util.stream.Stream.of; import static javax.management.ImmutableDescriptor.EMPTY_DESCRIPTOR; /** @@ -552,12 +557,29 @@ public static MBeanInfo getMBeanInfo(MBeanServer mBeanServer, ObjectName objectN } + /** + * @see MBeanOperationInfo#methodSignature(Method) + */ + public static MBeanParameterInfo[] methodSignature(Method method) { + Parameter[] parameters = method.getParameters(); + return of(parameters) + .map(MBeanParameterInfoBuilder::parameter) + .map(MBeanParameterInfoBuilder::build) + .toArray(MBeanParameterInfo[]::new); + } + + /** + * @see com.sun.jmx.mbeanserver.Introspector#descriptorForElement(AnnotatedElement) + */ @Nonnull public static Descriptor descriptorForElement(AnnotatedElement annotatedElement) { Annotation[] annotations = annotatedElement.getAnnotations(); return descriptorForAnnotations(annotations); } + /** + * @see com.sun.jmx.mbeanserver.Introspector#descriptorForAnnotations(Annotation[]) + */ @Nonnull public static Descriptor descriptorForAnnotations(Annotation[] annotations) { int length = length(annotations); diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java new file mode 100644 index 000000000..a04811ee2 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java @@ -0,0 +1,154 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + +import io.microsphere.annotation.Nonnull; + +import javax.management.Descriptor; +import javax.management.MBeanFeatureInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import java.lang.reflect.Method; +import java.util.List; +import java.util.function.Consumer; + +import static io.microsphere.collection.ListUtils.newLinkedList; +import static io.microsphere.management.JmxUtils.descriptorForElement; +import static io.microsphere.management.JmxUtils.methodSignature; +import static io.microsphere.management.builder.MBeanOperationInfoBuilder.Impact.UNKNOWN; +import static io.microsphere.management.builder.MBeanParameterInfoBuilder.parameter; +import static io.microsphere.util.ClassUtils.getTypeName; +import static java.util.Collections.addAll; + +/** + * {@link MBeanOperationInfo} Builder + * + * @author Mercy + * @see MBeanOperationInfo + * @see MBeanFeatureInfo + * @see MBeanFeatureInfoBuilder + * @since 1.0.0 + */ +public class MBeanOperationInfoBuilder extends MBeanFeatureInfoBuilder { + + /** + * The method's return value. + */ + @Nonnull + private String type; + + /** + * The signature of the method, that is, the class names + * of the arguments. + */ + private final List parameters = newLinkedList(); + + /** + * The impact of the method, one of {@link MBeanOperationInfo#INFO}, {@link MBeanOperationInfo#ACTION}, + * {@link MBeanOperationInfo#ACTION_INFO} and {@link MBeanOperationInfo#UNKNOWN}. + */ + private int impact; + + MBeanOperationInfoBuilder() { + super(); + impact(UNKNOWN); + } + + MBeanOperationInfoBuilder signature(MBeanParameterInfo[] signature) { + this.parameters.clear(); + addAll(this.parameters, signature); + return this; + } + + public MBeanOperationInfoBuilder param(Class type, Consumer parameterBuilderConsumer) { + MBeanParameterInfoBuilder builder = parameter(type); + parameterBuilderConsumer.accept(builder); + MBeanParameterInfo build = builder.build(); + this.parameters.add(build); + return this; + } + + public MBeanOperationInfoBuilder impact(Impact impact) { + this.impact = impact.getValue(); + return this; + } + + public MBeanOperationInfo build() { + return new MBeanOperationInfo(this.name, this.description, this.parameters.toArray(new MBeanParameterInfo[0]), + this.type, this.impact, this.descriptor); + } + + public static MBeanOperationInfoBuilder operation(Class type) { + return operation(getTypeName(type)); + } + + public static MBeanOperationInfoBuilder operation(String type) { + MBeanOperationInfoBuilder builder = new MBeanOperationInfoBuilder(); + builder.type = type; + return builder; + } + + public static MBeanOperationInfoBuilder operation(Method method) { + Class returnType = method.getReturnType(); + String name = method.getName(); + Descriptor descriptor = descriptorForElement(method); + MBeanParameterInfo[] signature = methodSignature(method); + return operation(returnType) + .name(name) + .signature(signature) + .descriptor(descriptor) + .description(method.toString()); + } + + public enum Impact { + + /** + * Indicates that the operation is read-like: + * it returns information but does not change any state. + */ + INFO(MBeanOperationInfo.INFO), + + /** + * Indicates that the operation is write-like: it has an effect but does + * not return any information from the MBean. + */ + ACTION(MBeanOperationInfo.ACTION), + + /** + * Indicates that the operation is both read-like and write-like: + * it has an effect, and it also returns information from the MBean. + */ + ACTION_INFO(MBeanOperationInfo.ACTION_INFO), + + /** + * Indicates that the impact of the operation is unknown or cannot be + * expressed using one of the other values. + */ + UNKNOWN(MBeanOperationInfo.UNKNOWN); + + private final int value; + + Impact(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java index 6a4487f7f..57acac63c 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java @@ -19,9 +19,13 @@ import io.microsphere.annotation.Nonnull; +import javax.management.Descriptor; import javax.management.MBeanFeatureInfo; import javax.management.MBeanParameterInfo; +import java.lang.annotation.Annotation; +import java.lang.reflect.Parameter; +import static io.microsphere.management.JmxUtils.descriptorForAnnotations; import static io.microsphere.util.ClassUtils.getTypeName; /** @@ -58,4 +62,14 @@ public static MBeanParameterInfoBuilder parameter(@Nonnull String type) { builder.type = type; return builder; } + + public static MBeanParameterInfoBuilder parameter(Parameter parameter) { + Annotation[] annotations = parameter.getAnnotations(); + Descriptor descriptor = descriptorForAnnotations(annotations); + String name = parameter.getName(); + return parameter(parameter.getType()) + .name(name) + .descriptor(descriptor) + .description(parameter.toString()); + } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java index 0b4e48698..df526d6f5 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java @@ -30,6 +30,7 @@ import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; +import javax.management.MBeanParameterInfo; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.ReflectionException; @@ -54,6 +55,7 @@ import static io.microsphere.management.JmxUtils.getOperatingSystemMXBean; import static io.microsphere.management.JmxUtils.getRuntimeMXBean; import static io.microsphere.management.JmxUtils.getThreadMXBean; +import static io.microsphere.management.JmxUtils.methodSignature; import static io.microsphere.reflect.MethodUtils.findMethod; import static io.microsphere.util.ArrayUtils.ofArray; import static java.lang.management.ManagementFactory.CLASS_LOADING_MXBEAN_NAME; @@ -283,6 +285,20 @@ void testDescriptorForAnnotationsOnEmptyAnnotations() { assertSame(EMPTY_DESCRIPTOR, descriptorForAnnotations(new Annotation[0])); } + @Test + void testMethodSignature() { + String methodName = "setCacheSize"; + Method method = findMethod(CacheControlMBean.class, methodName, long.class); + MBeanParameterInfo[] signature = methodSignature(method); + assertEquals(1, signature.length); + MBeanParameterInfo info = signature[0]; + + assertEquals("cacheSize", info.getName()); + assertEquals(long.class.getName(), info.getType()); + assertEquals("long cacheSize", info.getDescription()); + assertInstanceOf(ImmutableDescriptor.class, info.getDescriptor()); + } + private void assertPlatformMXBean(Optional platformMXBean, String name) throws Throwable { if (platformMXBean.isPresent()) { return; diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/Units.java b/microsphere-java-core/src/test/java/io/microsphere/management/Units.java index a4a4abba9..1033b278d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/Units.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/Units.java @@ -23,6 +23,7 @@ import java.lang.annotation.Target; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** @@ -33,7 +34,7 @@ * @since 1.0.0 */ @Documented -@Target(METHOD) +@Target({METHOD, PARAMETER}) @Retention(RUNTIME) public @interface Units { diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanOperationInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanOperationInfoBuilderTest.java new file mode 100644 index 000000000..dca5272aa --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanOperationInfoBuilderTest.java @@ -0,0 +1,83 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + + +import io.microsphere.management.CacheControlMBean; +import org.junit.jupiter.api.Test; + +import javax.management.ImmutableDescriptor; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import java.lang.reflect.Method; + +import static io.microsphere.management.builder.MBeanOperationInfoBuilder.Impact.ACTION; +import static io.microsphere.management.builder.MBeanOperationInfoBuilder.operation; +import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.util.ArrayUtils.ofArray; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link MBeanOperationInfoBuilder} Test + * + * @author Mercy + * @see MBeanOperationInfoBuilder + * @see AbstractMBeanFeatureInfoBuilderTest + * @since 1.0.0 + */ +class MBeanOperationInfoBuilderTest extends AbstractMBeanFeatureInfoBuilderTest { + + @Override + protected MBeanOperationInfoBuilder builder() { + Method method = findMethod(CacheControlMBean.class, "getCacheSize"); + return operation(method).description(TEST_DESCRIPTION); + } + + @Test + void testBuild() { + MBeanOperationInfo info = this.builder.build(); + assertEquals("getCacheSize", info.getName()); + assertEquals("long", info.getReturnType()); + assertArrayEquals(new MBeanParameterInfo[0], info.getSignature()); + assertEquals(TEST_DESCRIPTION, info.getDescription()); + + ImmutableDescriptor descriptor = (ImmutableDescriptor) info.getDescriptor(); + assertArrayEquals(ofArray("bytes"), descriptor.getFieldValues("units")); + } + + @Test + void testParam() { + String methodName = "testParam"; + Method method = findMethod(MBeanOperationInfoBuilderTest.class, methodName); + MBeanOperationInfoBuilder builder = operation(method) + .impact(ACTION) + .param(long.class, b -> b.name(TEST_NAME).description(TEST_DESCRIPTION)); + + MBeanOperationInfo build = builder.build(); + assertEquals(methodName, build.getName()); + assertEquals(ACTION.getValue(), build.getImpact()); + + MBeanParameterInfo[] signature = build.getSignature(); + assertEquals(1, signature.length); + + MBeanParameterInfo info = signature[0]; + assertEquals(TEST_NAME, info.getName()); + assertEquals(TEST_DESCRIPTION, info.getDescription()); + } +} \ No newline at end of file From cc507a676dc417986842346d8bde74fa8e55ecb2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 22:25:30 +0800 Subject: [PATCH 212/438] Add MBeanExecutableInfoBuilder Introduce an abstract MBeanExecutableInfoBuilder that extends MBeanFeatureInfoBuilder to construct MBean constructor/operation info. It manages parameter signature state, provides signature(...) and param(...) fluent methods, a from(Executable) helper to populate name/descriptor/signature/description from a reflection Executable, and a toSignature() helper. Also defines NO_PARAMS constant and integrates with MBeanParameterInfoBuilder and JmxUtils utilities. --- .../builder/MBeanExecutableInfoBuilder.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanExecutableInfoBuilder.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanExecutableInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanExecutableInfoBuilder.java new file mode 100644 index 000000000..52a2281e0 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanExecutableInfoBuilder.java @@ -0,0 +1,89 @@ + +/* + * 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. + */ + +package io.microsphere.management.builder; + +import io.microsphere.management.JmxUtils; + +import javax.management.Descriptor; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanFeatureInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import java.lang.reflect.Executable; +import java.util.List; +import java.util.function.Consumer; + +import static io.microsphere.collection.ListUtils.newLinkedList; +import static io.microsphere.management.JmxUtils.descriptorForElement; +import static io.microsphere.management.builder.MBeanParameterInfoBuilder.parameter; +import static java.util.Collections.addAll; + +/** + * MBean {@link Executable} Info Builder + * + * @author Mercy + * @see MBeanConstructorInfo + * @see MBeanOperationInfo + * @see MBeanFeatureInfo + * @see Executable + * @see MBeanFeatureInfoBuilder + * @since 1.0.0 + */ +public abstract class MBeanExecutableInfoBuilder extends MBeanFeatureInfoBuilder { + + protected static final MBeanParameterInfo[] NO_PARAMS = new MBeanParameterInfo[0]; + + /** + * The signature of the method, that is, the class names + * of the arguments. + */ + private final List parameters = newLinkedList(); + + MBeanExecutableInfoBuilder() { + super(); + } + + public B signature(MBeanParameterInfo... signature) { + this.parameters.clear(); + addAll(this.parameters, signature); + return (B) this; + } + + public B param(Class type, Consumer parameterBuilderConsumer) { + MBeanParameterInfoBuilder builder = parameter(type); + parameterBuilderConsumer.accept(builder); + MBeanParameterInfo build = builder.build(); + this.parameters.add(build); + return (B) this; + } + + public B from(Executable executable) { + String name = executable.getName(); + Descriptor descriptor = descriptorForElement(executable); + MBeanParameterInfo[] signature = JmxUtils.signature(executable.getParameters()); + return (B) name(name) + .signature(signature) + .descriptor(descriptor) + .description(executable.toString()); + } + + protected MBeanParameterInfo[] toSignature() { + return this.parameters.toArray(NO_PARAMS); + } +} \ No newline at end of file From decc687bdb405b625cd45130f341cc903c67ff3a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 22:25:39 +0800 Subject: [PATCH 213/438] Add MBeanConstructorInfoBuilder Introduce MBeanConstructorInfoBuilder to create javax.management.MBeanConstructorInfo instances. The builder extends MBeanExecutableInfoBuilder, provides a build() method that returns a MBeanConstructorInfo, and includes static factory methods constructor() and constructor(Constructor). Added Javadoc and Apache license header under package io.microsphere.management.builder. --- .../builder/MBeanConstructorInfoBuilder.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanConstructorInfoBuilder.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanConstructorInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanConstructorInfoBuilder.java new file mode 100644 index 000000000..8f84bfa17 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanConstructorInfoBuilder.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanFeatureInfo; +import java.lang.reflect.Constructor; + +/** + * {@link MBeanConstructorInfo} Builder + * + * @author Mercy + * @see MBeanConstructorInfo + * @see MBeanFeatureInfo + * @see MBeanFeatureInfoBuilder + * @since 1.0.0 + */ +public class MBeanConstructorInfoBuilder extends MBeanExecutableInfoBuilder { + + MBeanConstructorInfoBuilder() { + super(); + } + + public MBeanConstructorInfo build() { + return new MBeanConstructorInfo(this.name, this.description, toSignature()); + } + + public static MBeanConstructorInfoBuilder constructor() { + return new MBeanConstructorInfoBuilder(); + } + + public static MBeanConstructorInfoBuilder constructor(Constructor constructor) { + return constructor().from(constructor); + } +} \ No newline at end of file From 3b23f9efdd6c22631a911f233d289c84406e3660 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 22:25:49 +0800 Subject: [PATCH 214/438] Use MBeanExecutableInfoBuilder for operations Change MBeanOperationInfoBuilder to extend MBeanExecutableInfoBuilder and delegate executable/signature handling to the superclass. Removed the local parameters list and related methods (signature, param), updated build() to call toSignature(), and simplified operation(Method) to use .from(method). Also cleaned up unused imports. --- .../builder/MBeanOperationInfoBuilder.java | 43 ++----------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java index a04811ee2..36fefe274 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java @@ -19,21 +19,12 @@ import io.microsphere.annotation.Nonnull; -import javax.management.Descriptor; import javax.management.MBeanFeatureInfo; import javax.management.MBeanOperationInfo; -import javax.management.MBeanParameterInfo; import java.lang.reflect.Method; -import java.util.List; -import java.util.function.Consumer; -import static io.microsphere.collection.ListUtils.newLinkedList; -import static io.microsphere.management.JmxUtils.descriptorForElement; -import static io.microsphere.management.JmxUtils.methodSignature; import static io.microsphere.management.builder.MBeanOperationInfoBuilder.Impact.UNKNOWN; -import static io.microsphere.management.builder.MBeanParameterInfoBuilder.parameter; import static io.microsphere.util.ClassUtils.getTypeName; -import static java.util.Collections.addAll; /** * {@link MBeanOperationInfo} Builder @@ -44,7 +35,7 @@ * @see MBeanFeatureInfoBuilder * @since 1.0.0 */ -public class MBeanOperationInfoBuilder extends MBeanFeatureInfoBuilder { +public class MBeanOperationInfoBuilder extends MBeanExecutableInfoBuilder { /** * The method's return value. @@ -52,12 +43,6 @@ public class MBeanOperationInfoBuilder extends MBeanFeatureInfoBuilder parameters = newLinkedList(); - /** * The impact of the method, one of {@link MBeanOperationInfo#INFO}, {@link MBeanOperationInfo#ACTION}, * {@link MBeanOperationInfo#ACTION_INFO} and {@link MBeanOperationInfo#UNKNOWN}. @@ -69,28 +54,13 @@ public class MBeanOperationInfoBuilder extends MBeanFeatureInfoBuilder type, Consumer parameterBuilderConsumer) { - MBeanParameterInfoBuilder builder = parameter(type); - parameterBuilderConsumer.accept(builder); - MBeanParameterInfo build = builder.build(); - this.parameters.add(build); - return this; - } - public MBeanOperationInfoBuilder impact(Impact impact) { this.impact = impact.getValue(); return this; } public MBeanOperationInfo build() { - return new MBeanOperationInfo(this.name, this.description, this.parameters.toArray(new MBeanParameterInfo[0]), - this.type, this.impact, this.descriptor); + return new MBeanOperationInfo(this.name, this.description, toSignature(), this.type, this.impact, this.descriptor); } public static MBeanOperationInfoBuilder operation(Class type) { @@ -105,14 +75,7 @@ public static MBeanOperationInfoBuilder operation(String type) { public static MBeanOperationInfoBuilder operation(Method method) { Class returnType = method.getReturnType(); - String name = method.getName(); - Descriptor descriptor = descriptorForElement(method); - MBeanParameterInfo[] signature = methodSignature(method); - return operation(returnType) - .name(name) - .signature(signature) - .descriptor(descriptor) - .description(method.toString()); + return operation(returnType).from(method); } public enum Impact { From 28168eb3fdbbe347bd504c1fb452c7b4f877e116 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 22:26:01 +0800 Subject: [PATCH 215/438] Extract signature(Parameter[]) helper Add a new signature(Parameter[]) method that builds MBeanParameterInfo[] from an array of Parameters and refactor methodSignature(Method) to delegate to it. This consolidates parameter-processing logic into a reusable helper without changing behavior. --- .../src/main/java/io/microsphere/management/JmxUtils.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java index 641bbe158..dba771dee 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java @@ -561,7 +561,10 @@ public static MBeanInfo getMBeanInfo(MBeanServer mBeanServer, ObjectName objectN * @see MBeanOperationInfo#methodSignature(Method) */ public static MBeanParameterInfo[] methodSignature(Method method) { - Parameter[] parameters = method.getParameters(); + return signature(method.getParameters()); + } + + public static MBeanParameterInfo[] signature(Parameter[] parameters) { return of(parameters) .map(MBeanParameterInfoBuilder::parameter) .map(MBeanParameterInfoBuilder::build) From ebb62397c17eefb770f7fe1626bf13491a28d322 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Feb 2026 22:26:11 +0800 Subject: [PATCH 216/438] Add MBeanConstructorInfoBuilder unit tests Introduce MBeanConstructorInfoBuilderTest to verify MBeanConstructorInfoBuilder behavior. Tests cover param chaining and building from a reflective Constructor (String(byte[])), asserting the resulting MBeanConstructorInfo name, description, signature length and parameter name. The test class extends AbstractMBeanFeatureInfoBuilderTest. --- .../MBeanConstructorInfoBuilderTest.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanConstructorInfoBuilderTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanConstructorInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanConstructorInfoBuilderTest.java new file mode 100644 index 000000000..cfb27e808 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanConstructorInfoBuilderTest.java @@ -0,0 +1,70 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + + +import org.junit.jupiter.api.Test; + +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanParameterInfo; +import java.lang.reflect.Constructor; + +import static io.microsphere.management.builder.MBeanConstructorInfoBuilder.constructor; +import static io.microsphere.reflect.ConstructorUtils.findConstructor; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link MBeanConstructorInfoBuilder} Test + * + * @author Mercy + * @see MBeanConstructorInfoBuilder + * @see AbstractMBeanFeatureInfoBuilderTest + * @since 1.0.0 + */ +class MBeanConstructorInfoBuilderTest extends AbstractMBeanFeatureInfoBuilderTest { + + @Override + protected MBeanConstructorInfoBuilder builder() { + return constructor(); + } + + @Test + void testParam() { + assertSame(this.builder, this.builder.param(String.class, builder -> { + })); + } + + @Test + void testBuild() { + Constructor constructor = findConstructor(String.class, byte[].class); + MBeanConstructorInfo info = constructor(constructor).build(); + assertMBeanConstructorInfoBuilder(info, constructor); + } + + void assertMBeanConstructorInfoBuilder(MBeanConstructorInfo info, Constructor constructor) { + assertEquals(constructor.getName(), info.getName()); + assertEquals(constructor.toString(), info.getDescription()); + + MBeanParameterInfo[] signature = info.getSignature(); + assertEquals(1, signature.length); + + MBeanParameterInfo parameterInfo = signature[0]; + assertEquals("arg0", parameterInfo.getName()); + } +} \ No newline at end of file From b30d01e1a9d312cc6de4c594e05f7db75791c451 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 24 Feb 2026 11:17:40 +0800 Subject: [PATCH 217/438] Add MBeanNotificationInfoBuilder Introduce MBeanNotificationInfoBuilder to construct javax.management.MBeanNotificationInfo instances. The builder extends MBeanFeatureInfoBuilder, provides fluent setters, build() method, and static factory methods notification(Class...) and notification(String...). It normalizes Class inputs to type names via ClassUtils and safely handles null/empty type arrays. --- .../builder/MBeanNotificationInfoBuilder.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanNotificationInfoBuilder.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanNotificationInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanNotificationInfoBuilder.java new file mode 100644 index 000000000..0331e6360 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanNotificationInfoBuilder.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + +import io.microsphere.annotation.Nullable; +import io.microsphere.util.ClassUtils; + +import javax.management.MBeanFeatureInfo; +import javax.management.MBeanNotificationInfo; +import java.util.Objects; + +import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; +import static io.microsphere.util.ArrayUtils.isEmpty; +import static java.util.stream.Stream.of; + +/** + * {@link MBeanNotificationInfo} Builder + * + * @author Mercy + * @see MBeanFeatureInfoBuilder + * @see MBeanNotificationInfo + * @see MBeanFeatureInfo + * @since 1.0.0 + */ +public class MBeanNotificationInfoBuilder extends MBeanFeatureInfoBuilder { + + @Nullable + private String[] types; + + MBeanNotificationInfoBuilder types(@Nullable String... types) { + this.types = types; + return this; + } + + MBeanNotificationInfoBuilder() { + super(); + } + + public MBeanNotificationInfo build() { + return new MBeanNotificationInfo(this.types, this.name, this.description, this.descriptor); + } + + public static MBeanNotificationInfoBuilder notification(@Nullable Class... types) { + String[] classNames = getClassNames(types); + return notification(classNames); + } + + public static MBeanNotificationInfoBuilder notification(@Nullable String... types) { + return new MBeanNotificationInfoBuilder().types(types); + } + + static String[] getClassNames(Class[] types) { + if (isEmpty(types)) { + return EMPTY_STRING_ARRAY; + } + return of(types) + .filter(Objects::nonNull) + .map(ClassUtils::getTypeName) + .toArray(String[]::new); + } +} From 84c324a7cf75e4237c1c08a3839e274cc6184013 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 24 Feb 2026 11:17:52 +0800 Subject: [PATCH 218/438] Add MBeanNotificationInfoBuilderTest Introduce unit tests for MBeanNotificationInfoBuilder in microsphere-java-core. Covers building with name, description and descriptor, and verifies notification type handling using the notification(...) helper (including non-empty and empty class arrays), asserting expected name/description/descriptor and notif types. --- .../MBeanNotificationInfoBuilderTest.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanNotificationInfoBuilderTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanNotificationInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanNotificationInfoBuilderTest.java new file mode 100644 index 000000000..340fca7e4 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanNotificationInfoBuilderTest.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + + +import org.junit.jupiter.api.Test; + +import javax.management.MBeanNotificationInfo; + +import static io.microsphere.management.builder.MBeanNotificationInfoBuilder.getClassNames; +import static io.microsphere.management.builder.MBeanNotificationInfoBuilder.notification; +import static io.microsphere.util.ArrayUtils.EMPTY_CLASS_ARRAY; +import static io.microsphere.util.ArrayUtils.isEmpty; +import static io.microsphere.util.ArrayUtils.ofArray; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link MBeanNotificationInfoBuilder} Test + * + * @author Mercy + * @see MBeanNotificationInfoBuilder + * @since 1.0.0 + */ +class MBeanNotificationInfoBuilderTest extends AbstractMBeanFeatureInfoBuilderTest { + + @Test + void testBuild() { + this.builder.name(TEST_NAME); + this.builder.description(TEST_DESCRIPTION); + this.builder.descriptor(TEST_DESCRIPTOR); + + MBeanNotificationInfo info = this.builder.build(); + + assertEquals(TEST_NAME, info.getName()); + assertEquals(TEST_DESCRIPTION, info.getDescription()); + assertEquals(TEST_DESCRIPTOR, info.getDescriptor()); + assertTrue(isEmpty(info.getNotifTypes())); + } + + @Test + void testNotification() { + Class[] types = ofArray(String.class, Integer.class, long.class); + MBeanNotificationInfoBuilder builder = notification(types); + MBeanNotificationInfo info = builder.build(); + assertNull(info.getName()); + assertNull(info.getDescription()); + assertNotNull(info.getDescriptor()); + + assertArrayEquals(getClassNames(types), info.getNotifTypes()); + + builder = notification(EMPTY_CLASS_ARRAY); + info = builder.build(); + assertNull(info.getName()); + assertNull(info.getDescription()); + assertNotNull(info.getDescriptor()); + assertTrue(isEmpty(info.getNotifTypes())); + } +} \ No newline at end of file From 54acc83e384e2ba8ab6a58191061550132669ee0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 11:41:13 +0800 Subject: [PATCH 219/438] Add getter/setter/is method utilities Introduce constants for GET/SET/IS method name prefixes and add utility methods to detect JavaBean-style methods: isIsMethod, isGetterMethod, isSetterMethod. Also add small helper methods getMethodName, matchesParameterCount, matchesReturnType and isNoArgMethod, and import startsWith from StringUtils. Adjusted the comment for the banned methods property name. --- .../io/microsphere/reflect/MethodUtils.java | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java index c328564f2..8e5d47c1e 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java @@ -66,6 +66,7 @@ import static io.microsphere.util.ClassUtils.isArray; import static io.microsphere.util.ClassUtils.isPrimitive; import static io.microsphere.util.StringUtils.split; +import static io.microsphere.util.StringUtils.startsWith; import static io.microsphere.util.StringUtils.substringBefore; import static io.microsphere.util.StringUtils.substringBetween; import static io.microsphere.util.SystemUtils.getSystemProperty; @@ -84,7 +85,22 @@ public abstract class MethodUtils implements Utils { private static final Logger logger = getLogger(MethodUtils.class); /** - * The property name of banned methods + * The prefix of the "GETTER" method name : "get" + */ + public static final String GET_METHOD_NAME_PREFIX = "get"; + + /** + * The prefix of the "SETTER" method name : "set" + */ + public static final String SET_METHOD_NAME_PREFIX = "set"; + + /** + * The prefix of the "IS" method name : "is" + */ + public static final String IS_METHOD_NAME_PREFIX = "is"; + + /** + * The property name of banned methods : "microsphere.reflect.banned-methods" *

    Example Usage

    *
    {@code
          * System.setProperty(BANNED_METHODS_PROPERTY_NAME, "java.lang.String#substring() | java.lang.String#substring(int,int)")
    @@ -1270,6 +1286,51 @@ public static boolean isCallerSensitiveMethod(Method method) {
             return isAnnotationPresent(method, CALLER_SENSITIVE_ANNOTATION_CLASS);
         }
     
    +    public static boolean isIsMethod(Method method) {
    +        if (startsWith(getMethodName(method), IS_METHOD_NAME_PREFIX)) {
    +            if (isNoArgMethod(method)) {
    +                return matchesReturnType(method, boolean.class);
    +            }
    +        }
    +        return false;
    +    }
    +
    +    public static boolean isGetterMethod(Method method) {
    +        if (startsWith(getMethodName(method), GET_METHOD_NAME_PREFIX)) {
    +            if (isNoArgMethod(method)) {
    +                return !matchesReturnType(method, void.class);
    +            }
    +        }
    +        return false;
    +    }
    +
    +    public static boolean isSetterMethod(Method method) {
    +        if (startsWith(getMethodName(method), SET_METHOD_NAME_PREFIX)) {
    +            if (matchesParameterCount(method, 1)) {
    +                return matchesReturnType(method, void.class);
    +            }
    +        }
    +        return false;
    +    }
    +
    +    @Nullable
    +    public static String getMethodName(@Nullable Method method) {
    +        return method == null ? null : method.getName();
    +    }
    +
    +    public static boolean matchesParameterCount(@Nullable Method method, int parameterCount) {
    +        return method == null ? false : method.getParameterCount() == parameterCount;
    +    }
    +
    +    public static boolean matchesReturnType(@Nullable Method method, @Nullable Class returnType) {
    +        return method == null ? false : Objects.equals(returnType, method.getReturnType());
    +    }
    +
    +    static boolean isNoArgMethod(Method method) {
    +        return matchesParameterCount(method, 0);
    +    }
    +
    +
         static void filterDeclaredMethodsHierarchically(Class targetClass, Predicate methodToFilter, List methodsToCollect) {
             filterDeclaredMethods(targetClass, methodToFilter, methodsToCollect);
             for (Class interfaceClass : targetClass.getInterfaces()) {
    
    From 2a707e0236760409c16ab3f58fcb55bdd626cab8 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Wed, 25 Feb 2026 11:41:30 +0800
    Subject: [PATCH 220/438] Add MethodUtils unit tests and helpers
    
    Expand MethodUtilsTest with new unit tests and helpers: assert constants, OBJECT_* collections/predicates, is/get/set method detectors, getMethodName, matchesParameterCount, and matchesReturnType. Add helper assertion methods and getMethod lookup, plus small cleanup replacing a println with an assertion. New tiny methods (isMethod/get/set/setValue) and imports were added to support edge-case tests and improve coverage of MethodUtils behavior.
    ---
     .../microsphere/reflect/MethodUtilsTest.java  | 160 +++++++++++++++++-
     1 file changed, 158 insertions(+), 2 deletions(-)
    
    diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java
    index e85497343..1d5cc6ff3 100644
    --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java
    +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java
    @@ -17,10 +17,12 @@
     package io.microsphere.reflect;
     
     import io.microsphere.lang.Prioritized;
    +import io.microsphere.test.Data;
     import org.junit.jupiter.api.AfterAll;
     import org.junit.jupiter.api.BeforeEach;
     import org.junit.jupiter.api.Test;
     
    +import java.lang.management.RuntimeMXBean;
     import java.lang.reflect.Method;
     import java.util.AbstractList;
     import java.util.ArrayList;
    @@ -29,14 +31,19 @@
     import java.util.function.Predicate;
     
     import static io.microsphere.AbstractTestCase.JACOCO_AGENT_INSTRUCTED;
    +import static io.microsphere.collection.Lists.ofList;
     import static io.microsphere.reflect.MemberUtils.PUBLIC_MEMBER_PREDICATE;
     import static io.microsphere.reflect.MethodUtils.BANNED_METHODS_PROPERTY_NAME;
     import static io.microsphere.reflect.MethodUtils.FINAL_METHOD_PREDICATE;
    +import static io.microsphere.reflect.MethodUtils.GET_METHOD_NAME_PREFIX;
    +import static io.microsphere.reflect.MethodUtils.IS_METHOD_NAME_PREFIX;
     import static io.microsphere.reflect.MethodUtils.NON_PRIVATE_METHOD_PREDICATE;
     import static io.microsphere.reflect.MethodUtils.NON_STATIC_METHOD_PREDICATE;
     import static io.microsphere.reflect.MethodUtils.OBJECT_DECLARED_METHODS;
    +import static io.microsphere.reflect.MethodUtils.OBJECT_METHOD_PREDICATE;
     import static io.microsphere.reflect.MethodUtils.OBJECT_PUBLIC_METHODS;
     import static io.microsphere.reflect.MethodUtils.PUBLIC_METHOD_PREDICATE;
    +import static io.microsphere.reflect.MethodUtils.SET_METHOD_NAME_PREFIX;
     import static io.microsphere.reflect.MethodUtils.STATIC_METHOD_PREDICATE;
     import static io.microsphere.reflect.MethodUtils.banMethod;
     import static io.microsphere.reflect.MethodUtils.buildKey;
    @@ -53,12 +60,18 @@
     import static io.microsphere.reflect.MethodUtils.getAllDeclaredMethods;
     import static io.microsphere.reflect.MethodUtils.getAllMethods;
     import static io.microsphere.reflect.MethodUtils.getDeclaredMethods;
    +import static io.microsphere.reflect.MethodUtils.getMethodName;
     import static io.microsphere.reflect.MethodUtils.getMethods;
     import static io.microsphere.reflect.MethodUtils.getSignature;
     import static io.microsphere.reflect.MethodUtils.initBannedMethods;
     import static io.microsphere.reflect.MethodUtils.invokeMethod;
     import static io.microsphere.reflect.MethodUtils.invokeStaticMethod;
    +import static io.microsphere.reflect.MethodUtils.isGetterMethod;
    +import static io.microsphere.reflect.MethodUtils.isIsMethod;
     import static io.microsphere.reflect.MethodUtils.isObjectMethod;
    +import static io.microsphere.reflect.MethodUtils.isSetterMethod;
    +import static io.microsphere.reflect.MethodUtils.matchesParameterCount;
    +import static io.microsphere.reflect.MethodUtils.matchesReturnType;
     import static io.microsphere.reflect.MethodUtils.overrides;
     import static io.microsphere.util.ArrayUtils.EMPTY_CLASS_ARRAY;
     import static io.microsphere.util.ClassUtils.PRIMITIVE_TYPES;
    @@ -98,6 +111,24 @@ static void afterAll() {
             clearBannedMethods();
         }
     
    +    @Test
    +    void testConstants() {
    +        assertEquals("get", GET_METHOD_NAME_PREFIX);
    +        assertEquals("set", SET_METHOD_NAME_PREFIX);
    +        assertEquals("is", IS_METHOD_NAME_PREFIX);
    +        assertEquals("microsphere.reflect.banned-methods", BANNED_METHODS_PROPERTY_NAME);
    +
    +        assertEquals(ofList(Object.class.getMethods()), OBJECT_PUBLIC_METHODS);
    +        assertEquals(ofList(Object.class.getDeclaredMethods()), OBJECT_DECLARED_METHODS);
    +
    +        assertNotNull(OBJECT_METHOD_PREDICATE);
    +        assertNotNull(PUBLIC_METHOD_PREDICATE);
    +        assertNotNull(STATIC_METHOD_PREDICATE);
    +        assertNotNull(NON_STATIC_METHOD_PREDICATE);
    +        assertNotNull(FINAL_METHOD_PREDICATE);
    +        assertNotNull(NON_PRIVATE_METHOD_PREDICATE);
    +    }
    +
         @Test
         void testSTATIC_METHOD_PREDICATE() {
             assertTrue(STATIC_METHOD_PREDICATE.test(findMethod(ReflectionTest.class, "staticMethod")));
    @@ -547,10 +578,9 @@ void testFindNearestOverriddenMethod() {
         void test() {
             Method[] methods = TestSubInterface.class.getDeclaredMethods();
             methods = TestInterface.class.getDeclaredMethods();
    -        System.out.println(methods);
    +        assertNotNull(methods);
         }
     
    -
         /**
          * Test {@link MethodUtils#getSignature(Method)}
          */
    @@ -585,6 +615,119 @@ void testIsObjectMethod() {
             assertIsObjectMethod(false, String.class, "notFound");
         }
     
    +    @Test
    +    void testIsIsMethod() {
    +        Method method = findMethod(RuntimeMXBean.class, "isBootClassPathSupported");
    +        assertTrue(isIsMethod(method));
    +
    +        method = findMethod(MethodUtils.class, "isIsMethod", Method.class);
    +        assertFalse(isIsMethod(method));
    +
    +        method = getMethod("isMethod");
    +        assertFalse(isIsMethod(method));
    +
    +        method = getMethod("toString");
    +        assertFalse(isIsMethod(method));
    +
    +        assertFalse(isIsMethod(null));
    +    }
    +
    +    @Test
    +    void testIsGetterMethod() {
    +        Method method = findMethod(RuntimeMXBean.class, "getName");
    +        assertTrue(isGetterMethod(method));
    +
    +        method = findMethod(MethodUtils.class, "getMethodName", Method.class);
    +        assertFalse(isGetterMethod(method));
    +
    +        method = getMethod("get");
    +        assertFalse(isGetterMethod(method));
    +
    +        method = getMethod("toString");
    +        assertFalse(isGetterMethod(method));
    +
    +        assertFalse(isGetterMethod(null));
    +    }
    +
    +    @Test
    +    void testIsSetterMethod() {
    +        Method method = findMethod(Data.class, "setName", String.class);
    +        assertTrue(isSetterMethod(method));
    +
    +        method = getMethod("set");
    +        assertFalse(isSetterMethod(method));
    +
    +        method = getMethod("setValue", Object.class);
    +        assertFalse(isSetterMethod(method));
    +
    +        method = getMethod("toString");
    +        assertFalse(isSetterMethod(method));
    +
    +        assertFalse(isSetterMethod(null));
    +    }
    +
    +    @Test
    +    void testGetMethodName() {
    +        assertGetMethodName("toString");
    +        assertGetMethodName("hashCode");
    +        assertGetMethodName("clone");
    +        assertGetMethodName("not-found-method");
    +    }
    +
    +    @Test
    +    void testMatchesParameterCount() {
    +        assertMatchesParameterCount(0, "toString");
    +        assertMatchesParameterCount(1, "toString");
    +        assertMatchesParameterCount(0, "hashCode");
    +        assertMatchesParameterCount(1, "hashCode");
    +        assertMatchesParameterCount(1, "equals", Object.class);
    +        assertMatchesParameterCount(0, "not-found-method");
    +    }
    +
    +    @Test
    +    void testMatchesReturnType() {
    +        assertMatchesReturnType(String.class, "toString");
    +        assertMatchesReturnType(int.class, "hashCode");
    +        assertMatchesReturnType(boolean.class, "equals", Object.class);
    +        assertMatchesReturnType(void.class, "not-found-method");
    +    }
    +
    +    private void assertMatchesReturnType(Class expectedReturnType, String methodName, Class... parameterTypes) {
    +        Method method = getMethod(methodName, parameterTypes);
    +        if (method == null) {
    +            assertFalse(matchesReturnType(method, expectedReturnType));
    +        } else {
    +            assertTrue(matchesReturnType(method, expectedReturnType));
    +        }
    +    }
    +
    +    void assertMatchesParameterCount(int execptedCount, String methodName, Class... parameterTypes) {
    +        Method method = getMethod(methodName, parameterTypes);
    +        int parameterCount = parameterTypes.length;
    +        if (method == null) {
    +            assertFalse(matchesParameterCount(method, execptedCount));
    +        } else {
    +            if (execptedCount == parameterCount) {
    +                assertTrue(matchesParameterCount(method, execptedCount));
    +            } else {
    +                assertFalse(matchesParameterCount(method, execptedCount));
    +            }
    +        }
    +    }
    +
    +    void assertGetMethodName(String methodName) {
    +        Method method = getMethod(methodName);
    +        if (method == null) {
    +            assertNull(getMethodName(method));
    +        } else {
    +            assertEquals(methodName, getMethodName(method));
    +        }
    +    }
    +
    +    Method getMethod(String methodName, Class... parameterTypes) {
    +        return findMethod(MethodUtilsTest.class, methodName, parameterTypes);
    +    }
    +
         private void assertIsObjectMethod(boolean expected, Class declaredClass, String methodName, Class... parameterTypes) {
             Method method = findMethod(declaredClass, methodName, parameterTypes);
             assertEquals(expected, isObjectMethod(method));
    @@ -658,4 +801,17 @@ default void useLambda() {
         interface TestSubInterface extends TestInterface {
             void subMethod();
         }
    +
    +    void isMethod() {
    +    }
    +
    +    void get() {
    +    }
    +
    +    void set() {
    +    }
    +
    +    Object setValue(Object value) {
    +        return value;
    +    }
     }
    
    From 2dbff0dcfa7f1fb44d81a2d54ac2afe429575b6b Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Wed, 25 Feb 2026 15:46:47 +0800
    Subject: [PATCH 221/438] Make MBean attribute fields package-private
    
    Change MBeanAttributeInfoBuilder's attribute fields from private to package-private and rename attributeType to type. Update build() and attribute(...) to use the new field name. This relaxes visibility for package-level access and aligns the builder field naming.
    ---
     .../builder/MBeanAttributeInfoBuilder.java    | 20 +++++++++----------
     1 file changed, 10 insertions(+), 10 deletions(-)
    
    diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java
    index 437f07ac1..80b29c817 100644
    --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java
    +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanAttributeInfoBuilder.java
    @@ -34,25 +34,25 @@
     public class MBeanAttributeInfoBuilder extends MBeanFeatureInfoBuilder {
     
         /**
    -     * @serial The actual attribute type.
    +     * The actual attribute type.
          */
         @Nonnull
    -    private String attributeType;
    +    String type;
     
         /**
    -     * @serial The attribute write right.
    +     * The attribute write right.
          */
    -    private boolean write;
    +    boolean write;
     
         /**
    -     * @serial The attribute read right.
    +     * The attribute read right.
          */
    -    private boolean read;
    +    boolean read;
     
         /**
    -     * @serial Indicates if this method is a "is"
    +     * Indicates if this method is a "is"
          */
    -    private boolean is;
    +    boolean is;
     
         MBeanAttributeInfoBuilder() {
             super();
    @@ -74,7 +74,7 @@ public MBeanAttributeInfoBuilder is(boolean isIs) {
         }
     
         public MBeanAttributeInfo build() {
    -        return new MBeanAttributeInfo(this.name, this.attributeType, this.description, this.read, this.write,
    +        return new MBeanAttributeInfo(this.name, this.type, this.description, this.read, this.write,
                     this.is, this.descriptor);
         }
     
    @@ -85,7 +85,7 @@ public static MBeanAttributeInfoBuilder attribute(@Nonnull Class attributeTyp
         public static MBeanAttributeInfoBuilder attribute(@Nonnull String attributeType) {
             assertNotNull(attributeType, () -> "The 'attributeType' must not be null");
             MBeanAttributeInfoBuilder builder = new MBeanAttributeInfoBuilder();
    -        builder.attributeType = attributeType;
    +        builder.type = attributeType;
             return builder;
         }
     }
    \ No newline at end of file
    
    From 94e754243cece9bec5d739dd677ef6b686e3c059 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Wed, 25 Feb 2026 15:50:16 +0800
    Subject: [PATCH 222/438] Create MBeanDescribableBuilder.java
    
    ---
     .../builder/MBeanDescribableBuilder.java      | 62 +++++++++++++++++++
     1 file changed, 62 insertions(+)
     create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanDescribableBuilder.java
    
    diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanDescribableBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanDescribableBuilder.java
    new file mode 100644
    index 000000000..602b3f1aa
    --- /dev/null
    +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanDescribableBuilder.java
    @@ -0,0 +1,62 @@
    +/*
    + * 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.
    + */
    +
    +package io.microsphere.management.builder;
    +
    +import io.microsphere.annotation.Nullable;
    +
    +import javax.management.Descriptor;
    +import javax.management.DescriptorRead;
    +import javax.management.MBeanFeatureInfo;
    +
    +/**
    + * MBean Describable Builder
    + *
    + * @param  the builder class
    + * @author Mercy
    + * @see MBeanFeatureInfo
    + * @since 1.0.0
    + */
    +public abstract class MBeanDescribableBuilder {
    +
    +    /**
    +     * The human-readable description.
    +     **/
    +    @Nullable
    +    protected String description;
    +
    +    /**
    +     * This field can be null, which is equivalent to an empty Descriptor.
    +     */
    +    @Nullable
    +    protected Descriptor descriptor;
    +
    +    MBeanDescribableBuilder() {
    +    }
    +
    +    public B description(String description) {
    +        this.description = description;
    +        return (B) this;
    +    }
    +
    +    public B descriptor(Descriptor descriptor) {
    +        this.descriptor = descriptor;
    +        return (B) this;
    +    }
    +
    +    public abstract  I build();
    +}
    \ No newline at end of file
    
    From cdefd755fcc9551e08bac74d9458a6270cedc5c1 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Wed, 25 Feb 2026 15:50:24 +0800
    Subject: [PATCH 223/438] Add AbstractMBeanDescribableBuilderTest
    
    Introduce an abstract JUnit 5 test class for MBeanDescribableBuilder implementations. The test provides common tests for description and descriptor setters, a reflection-based builder instantiation helper, and declares an abstract testBuild() to be implemented by concrete subclasses. The test is disabled (abstract) and includes constants for test data.
    ---
     .../AbstractMBeanDescribableBuilderTest.java  | 74 +++++++++++++++++++
     1 file changed, 74 insertions(+)
     create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanDescribableBuilderTest.java
    
    diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanDescribableBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanDescribableBuilderTest.java
    new file mode 100644
    index 000000000..d0688efd5
    --- /dev/null
    +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanDescribableBuilderTest.java
    @@ -0,0 +1,74 @@
    +/*
    + * 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.
    + */
    +
    +package io.microsphere.management.builder;
    +
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.Disabled;
    +import org.junit.jupiter.api.Test;
    +
    +import javax.management.modelmbean.DescriptorSupport;
    +
    +import static io.microsphere.reflect.JavaType.from;
    +import static io.microsphere.util.ClassUtils.newInstance;
    +import static org.junit.jupiter.api.Assertions.assertEquals;
    +import static org.junit.jupiter.api.Assertions.assertSame;
    +
    +/**
    + * Abstract {@link MBeanDescribableBuilder} Test
    + *
    + * @author Mercy
    + * @see MBeanDescribableBuilder
    + * @since 1.0.0
    + */
    +@Disabled
    +abstract class AbstractMBeanDescribableBuilderTest {
    +
    +    static final String TEST_DESCRIPTION = "test-desc";
    +
    +    static final DescriptorSupport TEST_DESCRIPTOR = new DescriptorSupport();
    +
    +    protected B builder;
    +
    +    @BeforeEach
    +    void setUp() {
    +        this.builder = builder();
    +    }
    +
    +    protected B builder() {
    +        Class builderClass = from(getClass())
    +                .as(AbstractMBeanDescribableBuilderTest.class)
    +                .getGenericType(0)
    +                .toClass();
    +        return newInstance(builderClass);
    +    }
    +
    +    @Test
    +    void testDescription() {
    +        assertSame(this.builder, this.builder.description(TEST_DESCRIPTION));
    +        assertEquals(TEST_DESCRIPTION, this.builder.description);
    +    }
    +
    +    @Test
    +    void testDescriptor() {
    +        assertSame(this.builder, this.builder.descriptor(TEST_DESCRIPTOR));
    +        assertEquals(TEST_DESCRIPTOR, this.builder.descriptor);
    +    }
    +
    +    @Test
    +    abstract void testBuild();
    +}
    
    From 103996044384218e455e7476f2231f5e6cbb65dd Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Wed, 25 Feb 2026 15:50:46 +0800
    Subject: [PATCH 224/438] Make MBeanFeatureInfoBuilder extend base describable
     builder
    
    Refactor MBeanFeatureInfoBuilder to extend MBeanDescribableBuilder and remove duplicated description and descriptor fields and their setters. The name field is now annotated @Nullable and Javadoc was updated with a @param for the builder generic. This centralizes descriptor/description handling in the superclass and reduces duplicated code.
    ---
     .../builder/MBeanFeatureInfoBuilder.java      | 34 ++++---------------
     1 file changed, 7 insertions(+), 27 deletions(-)
    
    diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java
    index e899cf655..b572d3be9 100644
    --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java
    +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanFeatureInfoBuilder.java
    @@ -17,38 +17,28 @@
     
     package io.microsphere.management.builder;
     
    -import javax.management.Descriptor;
    +import io.microsphere.annotation.Nullable;
    +
     import javax.management.MBeanFeatureInfo;
     
     /**
      * {@link MBeanFeatureInfo} Builder
      *
    + * @param  the builder class
      * @author Mercy
      * @see MBeanFeatureInfo
      * @since 1.0.0
      */
    -public class MBeanFeatureInfoBuilder {
    +public class MBeanFeatureInfoBuilder extends MBeanDescribableBuilder {
     
         /**
          * The name of the feature.
    -     *
    -     * @serial The name of the feature.
    +     * 

    + * The name of the feature. */ + @Nullable protected String name; - /** - * The human-readable description of the feature. - * - * @serial The human-readable description of the feature. - */ - protected String description; - - /** - * @serial The Descriptor for this MBeanFeatureInfo. This field - * can be null, which is equivalent to an empty Descriptor. - */ - protected Descriptor descriptor; - MBeanFeatureInfoBuilder() { } @@ -57,16 +47,6 @@ public B name(String name) { return (B) this; } - public B description(String description) { - this.description = description; - return (B) this; - } - - public B descriptor(Descriptor descriptor) { - this.descriptor = descriptor; - return (B) this; - } - public MBeanFeatureInfo build() { return new MBeanFeatureInfo(this.name, this.description, this.descriptor); } From 10f11c3eb92d57a9cd4f86cc3364efee1c4da168 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:50:56 +0800 Subject: [PATCH 225/438] Inherit describable test base and simplify test Make AbstractMBeanFeatureInfoBuilderTest extend AbstractMBeanDescribableBuilderTest to reuse common test behavior and remove duplicated setup/code. Removed builder field, setup helper, TEST_DESCRIPTION and TEST_DESCRIPTOR constants, and dedicated tests for description and descriptor; adjusted imports and updated testBuild to reference TEST_NAME with the class qualifier. This reduces duplication and centralizes describable-related tests in the base test class. --- .../AbstractMBeanFeatureInfoBuilderTest.java | 41 ++----------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java index 3b564925e..e387ef345 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/AbstractMBeanFeatureInfoBuilderTest.java @@ -17,15 +17,11 @@ package io.microsphere.management.builder; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import javax.management.MBeanFeatureInfo; -import javax.management.modelmbean.DescriptorSupport; -import static io.microsphere.reflect.JavaType.from; -import static io.microsphere.util.ClassUtils.newInstance; import static javax.management.ImmutableDescriptor.EMPTY_DESCRIPTOR; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -39,47 +35,16 @@ * @since 1.0.0 */ @Disabled -abstract class AbstractMBeanFeatureInfoBuilderTest { +abstract class AbstractMBeanFeatureInfoBuilderTest extends AbstractMBeanDescribableBuilderTest { static final String TEST_NAME = "test-name"; - static final String TEST_DESCRIPTION = "test-desc"; - - static final DescriptorSupport TEST_DESCRIPTOR = new DescriptorSupport(); - - protected B builder; - - @BeforeEach - void setUp() { - this.builder = builder(); - } - - protected B builder() { - Class builderClass = from(getClass()) - .as(AbstractMBeanFeatureInfoBuilderTest.class) - .getGenericType(0) - .toClass(); - return newInstance(builderClass); - } - @Test void testName() { assertSame(this.builder, this.builder.name(TEST_NAME)); assertEquals(TEST_NAME, this.builder.name); } - @Test - void testDescription() { - assertSame(this.builder, this.builder.description(TEST_DESCRIPTION)); - assertEquals(TEST_DESCRIPTION, this.builder.description); - } - - @Test - void testDescriptor() { - assertSame(this.builder, this.builder.descriptor(TEST_DESCRIPTOR)); - assertEquals(TEST_DESCRIPTOR, this.builder.descriptor); - } - @Test void testBuild() { MBeanFeatureInfo info = this.builder.build(); @@ -87,9 +52,9 @@ void testBuild() { assertNull(info.getDescription()); assertSame(EMPTY_DESCRIPTOR, info.getDescriptor()); - assertSame(this.builder, this.builder.name(TEST_NAME)); + assertSame(this.builder, this.builder.name(AbstractMBeanFeatureInfoBuilderTest.TEST_NAME)); info = this.builder.build(); - assertSame(TEST_NAME, info.getName()); + assertSame(AbstractMBeanFeatureInfoBuilderTest.TEST_NAME, info.getName()); assertSame(this.builder, this.builder.description(TEST_DESCRIPTION)); info = this.builder.build(); From 430bb10e40a6efa3dcac74212920edde83c968c2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:51:05 +0800 Subject: [PATCH 226/438] Make parameters field protected Change visibility of the `parameters` field in MBeanExecutableInfoBuilder from private to protected so subclasses can access or modify the MBeanParameterInfo list. No other logic changes were made. --- .../management/builder/MBeanExecutableInfoBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanExecutableInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanExecutableInfoBuilder.java index 52a2281e0..d195544b2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanExecutableInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanExecutableInfoBuilder.java @@ -53,7 +53,7 @@ public abstract class MBeanExecutableInfoBuilder parameters = newLinkedList(); + protected final List parameters = newLinkedList(); MBeanExecutableInfoBuilder() { super(); From bb2950c32630e923c052d97e7a9d246a8d6a5819 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:51:16 +0800 Subject: [PATCH 227/438] Make types API public and add notification() Expose types(String...) and add an overload types(Class...) to accept Class inputs (converted to class names). Add a static notification() factory and update notification(...) overloads to delegate to it. Also relax visibility of the types field to allow builder usage. These changes improve the builder's fluent API and make constructing MBeanNotificationInfo instances from Class types more convenient. --- .../builder/MBeanNotificationInfoBuilder.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanNotificationInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanNotificationInfoBuilder.java index 0331e6360..b010275a9 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanNotificationInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanNotificationInfoBuilder.java @@ -40,28 +40,35 @@ public class MBeanNotificationInfoBuilder extends MBeanFeatureInfoBuilder { @Nullable - private String[] types; - - MBeanNotificationInfoBuilder types(@Nullable String... types) { - this.types = types; - return this; - } + String[] types; MBeanNotificationInfoBuilder() { super(); } + public MBeanNotificationInfoBuilder types(@Nullable Class... types) { + return types(getClassNames(types)); + } + + public MBeanNotificationInfoBuilder types(@Nullable String... types) { + this.types = types; + return this; + } + public MBeanNotificationInfo build() { return new MBeanNotificationInfo(this.types, this.name, this.description, this.descriptor); } + public static MBeanNotificationInfoBuilder notification() { + return new MBeanNotificationInfoBuilder(); + } + public static MBeanNotificationInfoBuilder notification(@Nullable Class... types) { - String[] classNames = getClassNames(types); - return notification(classNames); + return notification().types(types); } public static MBeanNotificationInfoBuilder notification(@Nullable String... types) { - return new MBeanNotificationInfoBuilder().types(types); + return notification().types(types); } static String[] getClassNames(Class[] types) { From e9375aeccb5692cf0f18ea310334e1710c23010c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:51:29 +0800 Subject: [PATCH 228/438] Rename 'type' to 'returnType' in builder Rename the field 'type' to 'returnType' in MBeanOperationInfoBuilder and update usages accordingly (build() and operation(String)). Also relax visibility of returnType and impact from private to package-private. This clarifies the field's purpose without changing behavior. --- .../management/builder/MBeanOperationInfoBuilder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java index 36fefe274..576cd103d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanOperationInfoBuilder.java @@ -41,13 +41,13 @@ public class MBeanOperationInfoBuilder extends MBeanExecutableInfoBuilder type) { @@ -69,7 +69,7 @@ public static MBeanOperationInfoBuilder operation(Class type) { public static MBeanOperationInfoBuilder operation(String type) { MBeanOperationInfoBuilder builder = new MBeanOperationInfoBuilder(); - builder.type = type; + builder.returnType = type; return builder; } From fdb9e1d6a5bf5192c4ca6107aabc2ac801192e35 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:51:47 +0800 Subject: [PATCH 229/438] Make type field package-private Relaxed the visibility of MBeanParameterInfoBuilder.type from private to package-private so other classes in the same package (such as related builders or tests) can access it. This is a non-functional change limited to field accessibility. --- .../management/builder/MBeanParameterInfoBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java index 57acac63c..ac5d43b18 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanParameterInfoBuilder.java @@ -43,7 +43,7 @@ public class MBeanParameterInfoBuilder extends MBeanFeatureInfoBuilder Date: Wed, 25 Feb 2026 15:52:00 +0800 Subject: [PATCH 230/438] Refactor notification assertions in test Consolidate notification-related assertions in MBeanNotificationInfoBuilderTest by introducing helper methods assertNotification(...) that validate MBeanNotificationInfo for both Class[] and String[] inputs (including EMPTY_CLASS_ARRAY). Remove unused ofArray import and centralize checks (null name/description, non-null descriptor, and notifTypes equality) to reduce duplication and improve clarity. --- .../MBeanNotificationInfoBuilderTest.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanNotificationInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanNotificationInfoBuilderTest.java index 340fca7e4..689d97782 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanNotificationInfoBuilderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanNotificationInfoBuilderTest.java @@ -26,7 +26,6 @@ import static io.microsphere.management.builder.MBeanNotificationInfoBuilder.notification; import static io.microsphere.util.ArrayUtils.EMPTY_CLASS_ARRAY; import static io.microsphere.util.ArrayUtils.isEmpty; -import static io.microsphere.util.ArrayUtils.ofArray; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -58,20 +57,26 @@ void testBuild() { @Test void testNotification() { - Class[] types = ofArray(String.class, Integer.class, long.class); + assertNotification(String.class, Integer.class, long.class); + + assertNotification(EMPTY_CLASS_ARRAY); + } + + void assertNotification(Class... types) { MBeanNotificationInfoBuilder builder = notification(types); - MBeanNotificationInfo info = builder.build(); - assertNull(info.getName()); - assertNull(info.getDescription()); - assertNotNull(info.getDescriptor()); + String[] classNames = getClassNames(types); + assertNotification(builder, classNames); - assertArrayEquals(getClassNames(types), info.getNotifTypes()); + builder = notification(classNames); + assertNotification(builder, classNames); + } - builder = notification(EMPTY_CLASS_ARRAY); - info = builder.build(); + void assertNotification(MBeanNotificationInfoBuilder builder, String... classNames) { + MBeanNotificationInfo info = builder.build(); assertNull(info.getName()); assertNull(info.getDescription()); assertNotNull(info.getDescriptor()); - assertTrue(isEmpty(info.getNotifTypes())); + + assertArrayEquals(classNames, info.getNotifTypes()); } } \ No newline at end of file From fe478cccbaa964747f701234b3828f52be4ae445 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:52:14 +0800 Subject: [PATCH 231/438] Add MBeanInfoBuilder for JMX MBeanInfo Introduce MBeanInfoBuilder to programmatically construct javax.management.MBeanInfo instances. Provides fluent methods to add attributes, operations, constructors and notifications, and can initialize from a java.beans.BeanInfo. Internally composes MBeanAttributeInfoBuilder, MBeanOperationInfoBuilder, MBeanConstructorInfoBuilder and MBeanNotificationInfoBuilder and assembles the resulting MBeanInfo arrays. --- .../management/builder/MBeanInfoBuilder.java | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanInfoBuilder.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanInfoBuilder.java new file mode 100644 index 000000000..3b14a79ef --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanInfoBuilder.java @@ -0,0 +1,188 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; + +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; +import java.beans.BeanInfo; +import java.beans.MethodDescriptor; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; + +import static io.microsphere.reflect.MethodUtils.isIsMethod; +import static io.microsphere.util.ClassUtils.getTypeName; +import static java.beans.Introspector.decapitalize; +import static java.util.Objects.nonNull; + +/** + * The {@link MBeanInfo} Builder + * + * @author Mercy + * @see MBeanInfo + * @since 1.0.0 + */ +public class MBeanInfoBuilder extends MBeanDescribableBuilder { + + /** + * The MBean qualified name + */ + @Nonnull + String className; + + @Nullable + List attributeBuilders = new LinkedList<>(); + + @Nullable + List operationBuilders = new LinkedList<>(); + + @Nullable + List constructorBuilders = new LinkedList<>(); + + @Nullable + List notificationBuilders = new LinkedList<>(); + + public MBeanInfoBuilder attribute(String attributeName, Class attributeType, Consumer builderConsumer) { + MBeanAttributeInfoBuilder builder = MBeanAttributeInfoBuilder.attribute(attributeType).name(attributeName); + builderConsumer.accept(builder); + this.attributeBuilders.add(builder); + return this; + } + + public MBeanInfoBuilder attribute(PropertyDescriptor propertyDescriptor) { + String propertyName = propertyDescriptor.getName(); + Class propertyType = propertyDescriptor.getPropertyType(); + String attributeName = decapitalize(propertyName); + return attribute(attributeName, propertyType, builder -> { + Method readMethod = propertyDescriptor.getReadMethod(); + builder.is(isIsMethod(readMethod)); + builder.read(nonNull(readMethod)); + builder.write(nonNull(propertyDescriptor.getWriteMethod())); + builder.description(propertyDescriptor.toString()); + }); + } + + public MBeanInfoBuilder operation(String methodName, Class returnType, Consumer builderConsumer) { + MBeanOperationInfoBuilder builder = MBeanOperationInfoBuilder.operation(returnType).name(methodName); + builderConsumer.accept(builder); + this.operationBuilders.add(builder); + return this; + } + + public MBeanInfoBuilder operation(MethodDescriptor methodDescriptor) { + Method method = methodDescriptor.getMethod(); + return operation(method); + } + + public MBeanInfoBuilder operation(Method method) { + MBeanOperationInfoBuilder builder = MBeanOperationInfoBuilder.operation(method); + this.operationBuilders.add(builder); + return this; + } + + public MBeanInfoBuilder constructor(Constructor constructor) { + return constructor(builder -> builder.from(constructor)); + } + + public MBeanInfoBuilder constructor(Consumer builderConsumer) { + MBeanConstructorInfoBuilder builder = MBeanConstructorInfoBuilder.constructor(); + builderConsumer.accept(builder); + this.constructorBuilders.add(builder); + return this; + } + + public MBeanInfoBuilder notification(Class... types) { + return notification(builder -> builder.types(types)); + } + + public MBeanInfoBuilder notification(Consumer builderConsumer) { + MBeanNotificationInfoBuilder builder = MBeanNotificationInfoBuilder.notification(); + builderConsumer.accept(builder); + this.notificationBuilders.add(builder); + return this; + } + + public MBeanInfo build() { + return new MBeanInfo(this.className, this.description, buildAttributes(), buildConstructors(), + buildOperations(), buildNotifications(), this.descriptor); + } + + private MBeanAttributeInfo[] buildAttributes() { + return this.attributeBuilders.stream() + .map(MBeanAttributeInfoBuilder::build) + .toArray(MBeanAttributeInfo[]::new); + } + + private MBeanConstructorInfo[] buildConstructors() { + return this.constructorBuilders.stream() + .map(MBeanConstructorInfoBuilder::build) + .toArray(MBeanConstructorInfo[]::new); + } + + private MBeanOperationInfo[] buildOperations() { + return this.operationBuilders.stream() + .map(MBeanOperationInfoBuilder::build) + .toArray(MBeanOperationInfo[]::new); + } + + private MBeanNotificationInfo[] buildNotifications() { + return this.notificationBuilders.stream() + .map(MBeanNotificationInfoBuilder::build) + .toArray(MBeanNotificationInfo[]::new); + } + + public static MBeanInfoBuilder mbeanInfo(String className) { + MBeanInfoBuilder builder = new MBeanInfoBuilder(); + builder.className = className; + return builder; + } + + public static MBeanInfoBuilder mbeanInfo(BeanInfo beanInfo) { + Class beanClass = beanInfo.getBeanDescriptor().getBeanClass(); + MBeanInfoBuilder builder = new MBeanInfoBuilder(); + + builder.className = getTypeName(beanClass); + builder.description = beanInfo.toString(); + + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { + builder.attribute(propertyDescriptor); + } + + MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors(); + for (MethodDescriptor methodDescriptor : methodDescriptors) { + builder.operation(methodDescriptor); + } + + Constructor[] constructors = beanClass.getDeclaredConstructors(); + for (Constructor constructor : constructors) { + builder.constructor(constructor); + } + + return builder; + } +} \ No newline at end of file From 5a2db2f1331fb2215be6677eaf172fd9881f108b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:52:27 +0800 Subject: [PATCH 232/438] Add MBeanInfoBuilderTest unit tests Introduce comprehensive unit tests for MBeanInfoBuilder. The new test file exercises attribute, operation, constructor, and notification builders, creation from BeanInfo, and the build() path producing MBeanInfo. Tests verify mapping from PropertyDescriptor and MethodDescriptor to builder fields and assert resulting MBeanInfo attributes, operations, and constructors using the Data bean metadata. --- .../builder/MBeanInfoBuilderTest.java | 262 ++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanInfoBuilderTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanInfoBuilderTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanInfoBuilderTest.java new file mode 100644 index 000000000..bfd1472cb --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/builder/MBeanInfoBuilderTest.java @@ -0,0 +1,262 @@ +/* + * 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. + */ + +package io.microsphere.management.builder; + + +import io.microsphere.beans.BeanMetadata; +import io.microsphere.test.Data; +import org.junit.jupiter.api.Test; + +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import java.beans.BeanInfo; +import java.beans.MethodDescriptor; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; + +import static io.microsphere.beans.BeanUtils.getBeanMetadata; +import static io.microsphere.management.builder.AbstractMBeanFeatureInfoBuilderTest.TEST_NAME; +import static io.microsphere.management.builder.MBeanInfoBuilder.mbeanInfo; +import static io.microsphere.management.builder.MBeanOperationInfoBuilder.Impact.ACTION; +import static io.microsphere.reflect.MethodUtils.isIsMethod; +import static io.microsphere.reflect.MethodUtils.isSetterMethod; +import static io.microsphere.util.ArrayUtils.forEach; +import static io.microsphere.util.ArrayUtils.ofArray; +import static io.microsphere.util.ClassUtils.getTypeName; +import static io.microsphere.util.StringUtils.uncapitalize; +import static java.util.Objects.nonNull; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link MBeanInfoBuilder} Test + * + * @author Mercy + * @see MBeanInfoBuilder + * @since 1.0.0 + */ +class MBeanInfoBuilderTest extends AbstractMBeanDescribableBuilderTest { + + @Test + void testAttribute() { + assertTrue(this.builder.attributeBuilders.isEmpty()); + + this.builder.attribute(TEST_NAME, String.class, b -> { + b.is(true); + b.read(true); + b.write(true); + }); + + assertEquals(1, this.builder.attributeBuilders.size()); + + MBeanAttributeInfoBuilder attributeInfoBuilder = this.builder.attributeBuilders.get(0); + assertEquals(TEST_NAME, attributeInfoBuilder.name); + assertEquals(String.class.getName(), attributeInfoBuilder.type); + assertTrue(attributeInfoBuilder.is); + assertTrue(attributeInfoBuilder.read); + assertTrue(attributeInfoBuilder.write); + assertNull(attributeInfoBuilder.description); + assertNull(attributeInfoBuilder.descriptor); + } + + @Test + void testOperation() { + assertTrue(this.builder.operationBuilders.isEmpty()); + + this.builder.operation(TEST_NAME, String.class, b -> { + b.impact(ACTION); + }); + + assertEquals(1, this.builder.operationBuilders.size()); + + MBeanOperationInfoBuilder operationInfoBuilder = this.builder.operationBuilders.get(0); + assertEquals(TEST_NAME, operationInfoBuilder.name); + assertEquals(String.class.getName(), operationInfoBuilder.returnType); + assertEquals(ACTION.getValue(), operationInfoBuilder.impact); + assertNull(operationInfoBuilder.description); + assertNull(operationInfoBuilder.descriptor); + } + + @Test + void testConstructor() { + assertTrue(this.builder.constructorBuilders.isEmpty()); + + this.builder.constructor(b -> { + b.name(TEST_NAME); + b.description(TEST_DESCRIPTION); + b.descriptor(TEST_DESCRIPTOR); + }); + + assertEquals(1, this.builder.constructorBuilders.size()); + + MBeanConstructorInfoBuilder constructorInfoBuilder = this.builder.constructorBuilders.get(0); + assertEquals(TEST_NAME, constructorInfoBuilder.name); + assertEquals(TEST_DESCRIPTION, constructorInfoBuilder.description); + assertEquals(TEST_DESCRIPTOR, constructorInfoBuilder.descriptor); + assertTrue(constructorInfoBuilder.parameters.isEmpty()); + } + + @Test + void testNotification() { + assertTrue(this.builder.notificationBuilders.isEmpty()); + + this.builder.notification(b -> { + b.name(TEST_NAME); + b.description(TEST_DESCRIPTION); + b.descriptor(TEST_DESCRIPTOR); + b.types(String.class); + }); + + assertEquals(1, this.builder.notificationBuilders.size()); + + MBeanNotificationInfoBuilder notificationInfoBuilder = this.builder.notificationBuilders.get(0); + assertEquals(TEST_NAME, notificationInfoBuilder.name); + assertEquals(TEST_DESCRIPTION, notificationInfoBuilder.description); + assertEquals(TEST_DESCRIPTOR, notificationInfoBuilder.descriptor); + assertArrayEquals(ofArray(String.class.getName()), notificationInfoBuilder.types); + } + + @Test + void testNotificationWithTypes() { + assertTrue(this.builder.notificationBuilders.isEmpty()); + + this.builder.notification(String.class); + + assertEquals(1, this.builder.notificationBuilders.size()); + + MBeanNotificationInfoBuilder notificationInfoBuilder = this.builder.notificationBuilders.get(0); + assertNull(notificationInfoBuilder.name); + assertNull(notificationInfoBuilder.description); + assertNull(notificationInfoBuilder.descriptor); + assertArrayEquals(ofArray(String.class.getName()), notificationInfoBuilder.types); + } + + @Test + void testMbeanInfo() { + MBeanInfoBuilder builder = mbeanInfo(String.class.getName()); + assertEquals(String.class.getName(), builder.className); + assertNull(builder.description); + assertNull(builder.descriptor); + } + + @Test + void testMbeanInfoWithBeanInfo() { + BeanMetadata beanMetadata = getBeanMetadata(Data.class); + BeanInfo beanInfo = beanMetadata.getBeanInfo(); + MBeanInfoBuilder builder = mbeanInfo(beanInfo); + + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors(); + + assertEquals(Data.class.getName(), builder.className); + assertEquals(beanInfo.toString(), builder.description); + + forEach(propertyDescriptors, (index, propertyDescriptor) -> { + assertAttribute(builder, index, propertyDescriptor); + }); + + forEach(methodDescriptors, (index, methodDescriptor) -> { + assertOperation(builder, index, methodDescriptor); + }); + + } + + @Test + void testBuild() { + BeanMetadata beanMetadata = getBeanMetadata(Data.class); + BeanInfo beanInfo = beanMetadata.getBeanInfo(); + MBeanInfoBuilder builder = mbeanInfo(beanInfo); + MBeanInfo info = builder.build(); + + assertEquals(getTypeName(Data.class), info.getClassName()); + assertEquals(beanInfo.toString(), info.getDescription()); + + forEach(info.getAttributes(), (index, attributeInfo) -> { + assertAttributeInfo(builder, index, attributeInfo); + }); + + forEach(info.getOperations(), (index, operationInfo) -> { + assertOperationInfo(builder, index, operationInfo); + }); + + forEach(info.getConstructors(), (index, constructorInfo) -> { + assertConstructorInfo(builder, index, constructorInfo); + }); + } + + private void assertAttributeInfo(MBeanInfoBuilder builder, Integer index, MBeanAttributeInfo attributeInfo) { + MBeanAttributeInfoBuilder attributeInfoBuilder = builder.attributeBuilders.get(index); + assertEquals(attributeInfo.getName(), attributeInfoBuilder.name); + assertEquals(attributeInfo.getType(), attributeInfoBuilder.type); + assertEquals(attributeInfo.isIs(), attributeInfoBuilder.is); + assertEquals(attributeInfo.isReadable(), attributeInfoBuilder.read); + assertEquals(attributeInfo.isWritable(), attributeInfoBuilder.write); + assertNull(attributeInfoBuilder.descriptor); + assertNotNull(attributeInfo.getDescriptor()); + assertEquals(attributeInfo.getDescription(), attributeInfoBuilder.description); + } + + private void assertOperationInfo(MBeanInfoBuilder builder, Integer index, MBeanOperationInfo operationInfo) { + MBeanOperationInfoBuilder operationInfoBuilder = builder.operationBuilders.get(index); + + assertEquals(operationInfo.getName(), operationInfoBuilder.name); + assertEquals(operationInfo.getImpact(), operationInfoBuilder.impact); + assertEquals(operationInfo.getReturnType(), operationInfoBuilder.returnType); + assertEquals(operationInfo.getDescription(), operationInfoBuilder.description); + assertEquals(operationInfo.getDescriptor(), operationInfoBuilder.descriptor); + } + + private void assertConstructorInfo(MBeanInfoBuilder builder, Integer index, MBeanConstructorInfo constructorInfo) { + MBeanConstructorInfoBuilder constructorInfoBuilder = builder.constructorBuilders.get(index); + + assertEquals(constructorInfo.getName(), constructorInfoBuilder.name); + assertEquals(constructorInfo.getDescription(), constructorInfoBuilder.description); + assertEquals(constructorInfo.getDescriptor(), constructorInfoBuilder.descriptor); + } + + private void assertAttribute(MBeanInfoBuilder builder, int index, PropertyDescriptor propertyDescriptor) { + MBeanAttributeInfoBuilder attributeInfoBuilder = builder.attributeBuilders.get(index); + String attributeName = uncapitalize(propertyDescriptor.getName()); + Class propertyType = propertyDescriptor.getPropertyType(); + Method readMethod = propertyDescriptor.getReadMethod(); + Method writeMethod = propertyDescriptor.getWriteMethod(); + + assertEquals(attributeName, attributeInfoBuilder.name); + assertEquals(getTypeName(propertyType), attributeInfoBuilder.type); + assertEquals(isIsMethod(readMethod), attributeInfoBuilder.is); + assertEquals(nonNull(readMethod), attributeInfoBuilder.read); + assertEquals(isSetterMethod(writeMethod), attributeInfoBuilder.write); + assertEquals(propertyDescriptor.toString(), attributeInfoBuilder.description); + } + + private void assertOperation(MBeanInfoBuilder builder, Integer index, MethodDescriptor methodDescriptor) { + MBeanOperationInfoBuilder operationInfoBuilder = builder.operationBuilders.get(index); + Method method = methodDescriptor.getMethod(); + String methodName = method.getName(); + Class returnType = method.getReturnType(); + + assertEquals(methodName, operationInfoBuilder.name); + assertEquals(getTypeName(returnType), operationInfoBuilder.returnType); + assertEquals(method.toString(), operationInfoBuilder.description); + } +} \ No newline at end of file From af25cb5f6ca3e7f637f4295351ed23b63c41f122 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:52:36 +0800 Subject: [PATCH 233/438] Add EMPTY_* MBeanInfo array constants Introduce shared empty array constants in JmxUtils for MBeanAttributeInfo, MBeanOperationInfo, MBeanConstructorInfo and MBeanNotificationInfo to avoid repeated allocations and provide a canonical empty value. Also add the corresponding imports for MBeanConstructorInfo and MBeanNotificationInfo. --- .../main/java/io/microsphere/management/JmxUtils.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java index dba771dee..bb404bdbc 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java @@ -30,7 +30,9 @@ import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MBeanServer; @@ -87,6 +89,14 @@ public abstract class JmxUtils implements Utils { public static final MBeanAttribute[] EMPTY_MBEAN_ATTRIBUTE_ARRAY = new MBeanAttribute[0]; + public static final MBeanAttributeInfo[] EMPTY_MBEAN_ATTRIBUTE_INFO_ARRAY = new MBeanAttributeInfo[0]; + + public static final MBeanOperationInfo[] EMPTY_MBEAN_OPERATION_INFO_ARRAY = new MBeanOperationInfo[0]; + + public static final MBeanConstructorInfo[] EMPTY_MBEAN_CONSTRUCTOR_INFO_ARRAY = new MBeanConstructorInfo[0]; + + public static final MBeanNotificationInfo[] EMPTY_MBEAN_NOTIFICATION_INFO_ARRAY = new MBeanNotificationInfo[0]; + private static ClassLoadingMXBean classLoadingMXBean; private static MemoryMXBean memoryMXBean; From 5c5bde9fee50f47a629231ce9d869b579a6816be Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 15:52:41 +0800 Subject: [PATCH 234/438] Add test for empty MBean constant arrays Import EMPTY_MBEAN_* constants and ArrayUtils.isEmpty, then add testConstants() to assert that the predefined MBean attribute/info/operation/constructor/notification arrays are empty. This increases test coverage and prevents regressions where those default arrays might become non-empty. --- .../io/microsphere/management/JmxUtilsTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java index df526d6f5..5e4455b66 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java @@ -41,6 +41,10 @@ import java.util.Optional; import static io.microsphere.management.JmxUtils.EMPTY_MBEAN_ATTRIBUTE_ARRAY; +import static io.microsphere.management.JmxUtils.EMPTY_MBEAN_ATTRIBUTE_INFO_ARRAY; +import static io.microsphere.management.JmxUtils.EMPTY_MBEAN_CONSTRUCTOR_INFO_ARRAY; +import static io.microsphere.management.JmxUtils.EMPTY_MBEAN_NOTIFICATION_INFO_ARRAY; +import static io.microsphere.management.JmxUtils.EMPTY_MBEAN_OPERATION_INFO_ARRAY; import static io.microsphere.management.JmxUtils.descriptorForAnnotations; import static io.microsphere.management.JmxUtils.descriptorForElement; import static io.microsphere.management.JmxUtils.getAttribute; @@ -57,6 +61,7 @@ import static io.microsphere.management.JmxUtils.getThreadMXBean; import static io.microsphere.management.JmxUtils.methodSignature; import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.util.ArrayUtils.isEmpty; import static io.microsphere.util.ArrayUtils.ofArray; import static java.lang.management.ManagementFactory.CLASS_LOADING_MXBEAN_NAME; import static java.lang.management.ManagementFactory.COMPILATION_MXBEAN_NAME; @@ -102,6 +107,15 @@ static void beforeAll() throws Throwable { notFoundAttributeName = "NotFound"; } + @Test + void testConstants() { + assertTrue(isEmpty(EMPTY_MBEAN_ATTRIBUTE_ARRAY)); + assertTrue(isEmpty(EMPTY_MBEAN_ATTRIBUTE_INFO_ARRAY)); + assertTrue(isEmpty(EMPTY_MBEAN_OPERATION_INFO_ARRAY)); + assertTrue(isEmpty(EMPTY_MBEAN_CONSTRUCTOR_INFO_ARRAY)); + assertTrue(isEmpty(EMPTY_MBEAN_NOTIFICATION_INFO_ARRAY)); + } + @Test void testGetClassLoadingMXBean() throws Throwable { assertPlatformMXBean(getClassLoadingMXBean(), CLASS_LOADING_MXBEAN_NAME); From e09525688c57b6abc9776f637507171330fbb782 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 17:05:05 +0800 Subject: [PATCH 235/438] Add CacheControl MBean implementation Add CacheControl class implementing CacheControlMBean under test sources. The class provides a cacheSize property with a setter and a getter that validates the value (throws IllegalStateException if negative). Includes Apache license header and author annotation; intended for testing management MBean behavior. --- .../microsphere/management/CacheControl.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/management/CacheControl.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/CacheControl.java b/microsphere-java-core/src/test/java/io/microsphere/management/CacheControl.java new file mode 100644 index 000000000..99ad1b723 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/management/CacheControl.java @@ -0,0 +1,43 @@ +/* + * 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. + */ + +package io.microsphere.management; + +/** + * {@link CacheControlMBean} Implementation + * + * @author Mercy + * @see CacheControlMBean + * @since 1.0.0 + */ +public class CacheControl implements CacheControlMBean { + + private long cacheSize; + + @Override + public long getCacheSize() { + if (cacheSize < 0) { + throw new IllegalStateException("The 'cacheSize' must be positive!"); + } + return cacheSize; + } + + @Override + public void setCacheSize(long cacheSize) { + this.cacheSize = cacheSize; + } +} \ No newline at end of file From 851215ad421a0abbe02fb68cfc840aaa2e5f923a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 17:05:17 +0800 Subject: [PATCH 236/438] Remove decapitalize usage in MBeanInfoBuilder Simplify attribute(PropertyDescriptor) by passing the property name directly and removing the unused java.beans.Introspector.decapitalize import. The decapitalize call was redundant since PropertyDescriptor.getName() already provides the intended attribute name, so the code and import are cleaned up. --- .../io/microsphere/management/builder/MBeanInfoBuilder.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanInfoBuilder.java b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanInfoBuilder.java index 3b14a79ef..ac61033d5 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanInfoBuilder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/builder/MBeanInfoBuilder.java @@ -36,7 +36,6 @@ import static io.microsphere.reflect.MethodUtils.isIsMethod; import static io.microsphere.util.ClassUtils.getTypeName; -import static java.beans.Introspector.decapitalize; import static java.util.Objects.nonNull; /** @@ -76,8 +75,7 @@ public MBeanInfoBuilder attribute(String attributeName, Class attributeType, public MBeanInfoBuilder attribute(PropertyDescriptor propertyDescriptor) { String propertyName = propertyDescriptor.getName(); Class propertyType = propertyDescriptor.getPropertyType(); - String attributeName = decapitalize(propertyName); - return attribute(attributeName, propertyType, builder -> { + return attribute(propertyName, propertyType, builder -> { Method readMethod = propertyDescriptor.getReadMethod(); builder.is(isIsMethod(readMethod)); builder.read(nonNull(readMethod)); From ca0fc02730f66d1ec9914202f4c1a744e6695080 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 17:05:27 +0800 Subject: [PATCH 237/438] Simplify JMX attribute lookup, add exception case Remove the doGetAttribute overload that accepted MBeanAttributeInfo and inline attribute lookup to always use the attribute name. This eliminates the prior pre-check for attribute readability and the findMBeanAttributeInfo path. Also include AttributeNotFoundException in the caught exceptions when calling MBeanServer.getAttribute to avoid letting that case propagate as an unexpected error. Update public getAttribute callers to use the simplified doGetAttribute signature. --- .../io/microsphere/management/JmxUtils.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java index bb404bdbc..fe1d18dfe 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/management/JmxUtils.java @@ -453,7 +453,7 @@ public static MBeanAttribute[] getMBeanAttributes(MBeanServer mBeanServer, Objec */ @Nullable public static Object getAttribute(MBeanServer mBeanServer, ObjectName objectName, MBeanAttributeInfo attributeInfo) { - return doGetAttribute(mBeanServer, objectName, attributeInfo, attributeInfo.getName()); + return doGetAttribute(mBeanServer, objectName, attributeInfo.getName()); } /** @@ -481,7 +481,7 @@ public static Object getAttribute(MBeanServer mBeanServer, ObjectName objectName */ @Nullable public static Object getAttribute(MBeanServer mBeanServer, ObjectName objectName, String attributeName) { - return doGetAttribute(mBeanServer, objectName, null, attributeName); + return doGetAttribute(mBeanServer, objectName, attributeName); } /** @@ -618,21 +618,11 @@ public static Descriptor descriptorForAnnotations(Annotation[] annotations) { return EMPTY_DESCRIPTOR; } - protected static Object doGetAttribute(MBeanServer mBeanServer, ObjectName objectName, - @Nullable MBeanAttributeInfo attributeInfo, String attributeName) { - MBeanAttributeInfo mBeanAttributeInfo = attributeInfo; - if (mBeanAttributeInfo == null) { - mBeanAttributeInfo = findMBeanAttributeInfo(mBeanServer, objectName, attributeName); - } - - if (mBeanAttributeInfo == null || !mBeanAttributeInfo.isReadable()) { - return null; - } - + protected static Object doGetAttribute(MBeanServer mBeanServer, ObjectName objectName, String attributeName) { Object attributeValue = null; try { attributeValue = mBeanServer.getAttribute(objectName, attributeName); - } catch (ReflectionException | InstanceNotFoundException e) { + } catch (ReflectionException | InstanceNotFoundException | AttributeNotFoundException e) { handleException(e, mBeanServer, objectName); } catch (Throwable e) { throw wrap(e, RuntimeException.class); From 13afe92d185f7ea446ed6f07e0092adb1c355c59 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 17:05:38 +0800 Subject: [PATCH 238/438] Enhance JmxUtils tests for errors and lookups Expand test coverage in JmxUtilsTest by adding and updating tests for various error and lookup scenarios. Add handling for ReflectionException thrown by DynamicMBean#getAttribute, an InstanceNotFound case, and an Execution/RuntimeMBeanException case via a StandardMBean. Introduce tests for findMBeanAttributeInfo including successful lookup and not-found cases. Update imports to support new assertions, builders, and MBean classes. --- .../microsphere/management/JmxUtilsTest.java | 61 ++++++++++++++++--- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java index 5e4455b66..afc103f14 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java @@ -34,10 +34,13 @@ import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.ReflectionException; +import javax.management.RuntimeMBeanException; +import javax.management.StandardMBean; import java.lang.annotation.Annotation; import java.lang.management.PlatformManagedObject; import java.lang.reflect.Method; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import static io.microsphere.management.JmxUtils.EMPTY_MBEAN_ATTRIBUTE_ARRAY; @@ -47,6 +50,7 @@ import static io.microsphere.management.JmxUtils.EMPTY_MBEAN_OPERATION_INFO_ARRAY; import static io.microsphere.management.JmxUtils.descriptorForAnnotations; import static io.microsphere.management.JmxUtils.descriptorForElement; +import static io.microsphere.management.JmxUtils.findMBeanAttributeInfo; import static io.microsphere.management.JmxUtils.getAttribute; import static io.microsphere.management.JmxUtils.getClassLoadingMXBean; import static io.microsphere.management.JmxUtils.getCompilationMXBean; @@ -60,6 +64,7 @@ import static io.microsphere.management.JmxUtils.getRuntimeMXBean; import static io.microsphere.management.JmxUtils.getThreadMXBean; import static io.microsphere.management.JmxUtils.methodSignature; +import static io.microsphere.management.builder.MBeanInfoBuilder.mbeanInfo; import static io.microsphere.reflect.MethodUtils.findMethod; import static io.microsphere.util.ArrayUtils.isEmpty; import static io.microsphere.util.ArrayUtils.ofArray; @@ -81,6 +86,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -220,19 +226,17 @@ void testGetAttributeOnAttributeNotFound() { } @Test - void testGetAttributeOnUnreadableAttribute() throws Exception { - String attributeName = "a"; - String descrption = "NoDesc"; - MBeanAttributeInfo attribute = new MBeanAttributeInfo(attributeName, "java.lang.String", descrption, false, false, false); - MBeanAttributeInfo[] attributes = ofArray(attribute); + void testGetAttributeOnReflectionException() throws Exception { + String attributeName = "test"; - MBeanInfo mBeanInfo = new MBeanInfo("Test", descrption, attributes, ofArray(), ofArray(), ofArray()); - ObjectName objectName = getInstance("io.microsphere.management:type=Test"); + MBeanInfo mBeanInfo = mbeanInfo("Test") + .build(); + ObjectName objectName = getInstance("io.microsphere.management:type=Test"); DynamicMBean dynamicMBean = new DynamicMBean() { @Override public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { - return null; + throw new ReflectionException(new Exception(), "For testing"); } @Override @@ -268,8 +272,47 @@ public MBeanInfo getMBeanInfo() { } @Test - void testGetAttributeOnReflectionException() { + void testGetAttributeOnInstanceNotFoundException() throws Exception { + ObjectName objectName = getInstance("io.microsphere.management:type=Test"); + Object value = getAttribute(mBeanServer, objectName, notFoundAttributeName); + assertNull(value); + } + + @Test + void testGetAttributeOnExecutionException() throws Exception { + String attributeName = "CacheSize"; + + ObjectName objectName = getInstance("io.microsphere.management:type=CacheControl"); + CacheControl cacheControl = new CacheControl(); + + cacheControl.setCacheSize(-1); + + StandardMBean standardMBean = new StandardMBean(cacheControl, CacheControlMBean.class); + mBeanServer.registerMBean(standardMBean, objectName); + + assertThrows(RuntimeMBeanException.class, () -> getAttribute(mBeanServer, objectName, attributeName)); + } + + @Test + void testFindMBeanAttributeInfo() { + Map mBeanAttributesMap = getMBeanAttributesMap(mBeanServer, objectName); + for (Entry entry : mBeanAttributesMap.entrySet()) { + MBeanAttribute mBeanAttribute = entry.getValue(); + MBeanAttributeInfo mBeanAttributeInfo = findMBeanAttributeInfo(mBeanServer, objectName, entry.getKey()); + assertEquals(mBeanAttribute.getAttributeInfo(), mBeanAttributeInfo); + } + } + + @Test + void testFindMBeanAttributeInfoOnNotFoundMBeanInfo() { + MBeanAttributeInfo mBeanAttributeInfo = findMBeanAttributeInfo(mBeanServer, notFoundObjectName, notFoundAttributeName); + assertNull(mBeanAttributeInfo); + } + @Test + void testFindMBeanAttributeInfoOnNotFoundAttribute() { + MBeanAttributeInfo mBeanAttributeInfo = findMBeanAttributeInfo(mBeanServer, objectName, notFoundAttributeName); + assertNull(mBeanAttributeInfo); } @Test From 4df773af4550d8da522c77471536c81a67027ba5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 17:22:28 +0800 Subject: [PATCH 239/438] Use ArrayUtils.length and reorder offset assignment Add static import for ArrayUtils.length and replace manual args null check with length(args). Move the placeholder offset initialization after computing argsLength and the zero-args check to avoid unnecessary work. Minor refactor and import cleanup with no functional change. --- .../src/main/java/io/microsphere/text/FormatUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/text/FormatUtils.java b/microsphere-java-core/src/main/java/io/microsphere/text/FormatUtils.java index d7e08254e..1517616ae 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/text/FormatUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/text/FormatUtils.java @@ -2,6 +2,7 @@ import io.microsphere.util.Utils; +import static io.microsphere.util.ArrayUtils.length; import static io.microsphere.util.StringUtils.isBlank; import static java.lang.String.valueOf; @@ -62,11 +63,11 @@ public static String formatWithPlaceholder(String pattern, String placeholder, O if (isBlank(pattern)) { return pattern; } - int offset = placeholder.length(); - int argsLength = args == null ? 0 : args.length; + int argsLength = length(args); if (argsLength == 0) { return pattern; } + int offset = placeholder.length(); StringBuilder stringBuilder = new StringBuilder(pattern); int index = -1; for (int i = 0; i < argsLength; i++) { From cbbe05ea287363c9a2debab8570d48c2a63e8061 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:36:34 +0800 Subject: [PATCH 240/438] Replace logger field with inherited log() Remove the private static Logger declaration from ListUtilsTest and replace logger.trace(...) calls with the inherited log(...) helper. Cleans up an unused field and standardizes test logging; no behavioral changes. --- .../test/java/io/microsphere/collection/ListUtilsTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java index 8a38fcbe2..a204626dd 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java @@ -60,8 +60,6 @@ */ class ListUtilsTest extends AbstractTestCase { - private static final Logger logger = getLogger(ListUtilsTest.class); - private static final List TEST_LIST = asList("A", "B", "C"); @Test @@ -190,7 +188,7 @@ void testForEach() { MutableInteger mutableInteger = MutableInteger.of(0); Iterator iterator = list.iterator(); forEach(list, (index, value) -> { - logger.trace("forEach(index = {} , value = '{}')", index, value); + log("forEach(index = {} , value = '{}')", index, value); assertEquals(index, mutableInteger.getAndIncrement()); assertTrue(iterator.hasNext()); assertEquals(iterator.next(), value); @@ -198,7 +196,7 @@ void testForEach() { Iterator iterator2 = list.iterator(); forEach(list, (value) -> { - logger.trace("forEach(value = '{}')", value); + log("forEach(value = '{}')", value); assertTrue(iterator2.hasNext()); assertEquals(iterator2.next(), value); }); From 446e820448fe9a7380b62fd56678f7125f854e66 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:36:46 +0800 Subject: [PATCH 241/438] Use AbstractTestCase.log in executor test Make DelegatingScheduledExecutorServiceTest extend AbstractTestCase and use its log(...) helper instead of an explicit Logger. Removed Logger imports and the static logger field, and replaced logger.debug(...) calls with log(...). No change to test behavior. --- .../DelegatingScheduledExecutorServiceTest.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java index 7cef01079..8c7e6ca9f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java @@ -1,6 +1,6 @@ package io.microsphere.concurrent; -import io.microsphere.logging.Logger; +import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; import java.util.List; @@ -8,7 +8,6 @@ import java.util.concurrent.ScheduledExecutorService; import static io.microsphere.collection.Lists.ofList; -import static io.microsphere.logging.LoggerFactory.getLogger; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -24,9 +23,7 @@ * @see DelegatingScheduledExecutorService * @since 1.0.0 */ -class DelegatingScheduledExecutorServiceTest { - - private static final Logger logger = getLogger(DelegatingScheduledExecutorServiceTest.class); +class DelegatingScheduledExecutorServiceTest extends AbstractTestCase { private ScheduledExecutorService delegate = newSingleThreadScheduledExecutor(); @@ -43,7 +40,7 @@ void test() throws Throwable { // test schedule(Runnable...) Future future = instance.schedule(() -> { - logger.debug("schedule"); + log("schedule"); }, 1, MILLISECONDS); assertNull(future.get()); @@ -54,20 +51,20 @@ void test() throws Throwable { // test scheduleAtFixedRate future = instance.scheduleAtFixedRate(() -> { - logger.debug("scheduleAtFixedRate"); + log("scheduleAtFixedRate"); }, 1, 1, MILLISECONDS); future.cancel(true); // test scheduleWithFixedDelay future = instance.scheduleWithFixedDelay(() -> { - logger.debug("scheduleWithFixedDelay"); + log("scheduleWithFixedDelay"); }, 1, 1, MILLISECONDS); // test submit future = instance.submit(() -> { - logger.debug("submit"); + log("submit"); }); future.get(); @@ -93,7 +90,7 @@ void test() throws Throwable { // test execute instance.execute(() -> { - logger.debug("execute"); + log("execute"); }); // test shutdownNow From 84fb3480fdbb4e5dc219304052f8de5a88855a42 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:37:02 +0800 Subject: [PATCH 242/438] Make AbstractEventListener extend AbstractTestCase Have AbstractEventListener inherit from AbstractTestCase to reuse test utilities. Remove the local static Logger and replace logger.info(...) calls with the inherited log(...) method. Added import for AbstractTestCase and adjusted println accordingly. --- .../java/io/microsphere/event/AbstractEventListener.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java b/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java index bc03d934e..ade42fd8e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java @@ -16,15 +16,14 @@ */ package io.microsphere.event; +import io.microsphere.AbstractTestCase; import io.microsphere.logging.Logger; import java.util.concurrent.atomic.AtomicInteger; import static io.microsphere.logging.LoggerFactory.getLogger; -public abstract class AbstractEventListener implements EventListener { - - private static final Logger logger = getLogger(AbstractEventListener.class); +public abstract class AbstractEventListener extends AbstractTestCase implements EventListener { private final AtomicInteger eventOccurs = new AtomicInteger(0); @@ -41,6 +40,6 @@ public int getEventOccurs() { } protected void println(String message) { - logger.info("[{}] {}", Thread.currentThread().getName(), message); + log("[{}] {}", Thread.currentThread().getName(), message); } } \ No newline at end of file From 7d9d7ca46bf41702c428d45b176670b3f7603dfe Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:37:12 +0800 Subject: [PATCH 243/438] Use log(...) instead of logger.trace in test Replace logger.trace(e.getMessage(), e) with log(e.getMessage(), e) in FileUtilsTest's catch block. This updates the logging call in the test code and is intended as a simplification of the logging invocation without changing behavior. --- .../src/test/java/io/microsphere/io/FileUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index b0bb1bf39..b0c8361e3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -344,7 +344,7 @@ File handleDirectoryOnIOException0(ThrowableConsumer directoryHandler) thr ioExceptionReference.set(e); creatingFile.set(false); deletingDirectory.set(false); - logger.trace(e.getMessage(), e); + log(e.getMessage(), e); break; } catch (Throwable e) { throw wrap(e, Exception.class); From ad1a2fed7a986728da9e2cedb6a7f218dc644361 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:37:20 +0800 Subject: [PATCH 244/438] Replace logger.trace guard with log() call Simplify exception handling in StandardFileWatchServiceTest by removing the logger.isTraceEnabled() conditional and calling log(...) directly when the async timeout exception is caught. This cleans up the test code and ensures the exception is always logged before cancelling the future. --- .../java/io/microsphere/io/StandardFileWatchServiceTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java index 6d6c0763d..f7d1a65a3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java @@ -256,9 +256,7 @@ private void async(ThrowableAction task) { try { future.get(100, MILLISECONDS); } catch (Exception e) { - if (logger.isTraceEnabled()) { - logger.trace("Failed to async(timeout : 100ms) : {}", e.getMessage(), e); - } + log("Failed to async(timeout : 100ms) : {}", e.getMessage(), e); future.cancel(true); } } From f811e75ecdfc7b791109ca3328b8b80e5e9b992d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:37:32 +0800 Subject: [PATCH 245/438] Use log() helper in ThrowableActionTest Replace direct logger.trace(...) call with the inherited log(...) helper in ThrowableActionTest to align with test logging conventions from AbstractTestCase. No functional changes; improves consistency in test code. --- .../java/io/microsphere/lang/function/ThrowableActionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java index 4fb32ef8a..8b7786c04 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java @@ -30,7 +30,7 @@ class ThrowableActionTest extends AbstractTestCase { private final ThrowableAction action = () -> { - logger.trace("ThrowableAction#execute()"); + log("ThrowableAction#execute()"); }; private final ThrowableAction exceptionalAction = () -> { From 7a92fb4401ba8add5227f198d56775be482c1d33 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:37:40 +0800 Subject: [PATCH 246/438] Use log() helper in ThrowableBiConsumerTest Replace direct logger.info calls with the inherited log(...) helper in ThrowableBiConsumerTest. Updated occurrences in testAccept and testAndThen to centralize test logging; no behavior changes intended. --- .../microsphere/lang/function/ThrowableBiConsumerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java index 575d43208..b45b6c2ac 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java @@ -32,7 +32,7 @@ class ThrowableBiConsumerTest extends AbstractTestCase { @Test void testAccept() throws Throwable { ThrowableBiConsumer consumer = (s, i) -> { - logger.info("The String is : {}, the Integer is : {}", s, i); + log("The String is : {}, the Integer is : {}", s, i); }; consumer.accept("Mercy", 1); } @@ -40,10 +40,10 @@ void testAccept() throws Throwable { @Test void testAndThen() throws Throwable { ThrowableBiConsumer consumer = (s, i) -> { - logger.info("The String is : {}, the Integer is : {}", s, i); + log("The String is : {}, the Integer is : {}", s, i); }; consumer.andThen((s, i) -> { - logger.info("andThen -> The String is : {}, the Integer is : {}", s, i); + log("andThen -> The String is : {}, the Integer is : {}", s, i); }).accept("Mercy", 1); } } From 1c41d3573e9e940d874db43f67d4d08482621cbc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:37:54 +0800 Subject: [PATCH 247/438] Extend test class with AbstractTestCase Update AbstractReflectiveDefinitionTest to extend AbstractTestCase and add the required import. This lets the test inherit shared setup/utilities from AbstractTestCase. --- .../microsphere/reflect/AbstractReflectiveDefinitionTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java index fb87a8127..cfc33edc2 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java @@ -1,5 +1,6 @@ package io.microsphere.reflect; +import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,7 +30,7 @@ * @see ReflectiveDefinition * @since 1.0.0 */ -public abstract class AbstractReflectiveDefinitionTest { +public abstract class AbstractReflectiveDefinitionTest extends AbstractTestCase { private final List[] headConstructorArgumentsArray = ofArray( ofList(SINCE, getClassName()), From 2aae1e8e5edf559bb337165fcd68b84a1d76c377 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:38:06 +0800 Subject: [PATCH 248/438] Remove logging from MethodDefinitionTest Remove unused logging from MethodDefinitionTest: delete Logger import, getLogger static import, the logger field, and the private log helper method. Cleans up test class by eliminating unused dependencies and dead code. --- .../io/microsphere/reflect/MethodDefinitionTest.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java index 18eaf093a..919867c1b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java @@ -16,13 +16,11 @@ */ package io.microsphere.reflect; -import io.microsphere.logging.Logger; import org.junit.jupiter.api.Test; import java.util.List; import static io.microsphere.collection.Lists.ofList; -import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.ArrayUtils.ofArray; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -36,8 +34,6 @@ */ class MethodDefinitionTest extends AbstractExecutableDefinitionTest { - private static final Logger logger = getLogger(MethodDefinitionTest.class); - @Override protected List getTailConstructorArguments() { return ofList("log", ofArray("java.lang.String")); @@ -63,11 +59,4 @@ void testInvoke() { assertNull(definition.invoke(this, definition.toString())); } } - - - private void log(String message) { - if (logger.isDebugEnabled()) { - logger.debug(message); - } - } } From 08ea11226b4e28983865156d3c088dd45249dece Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:38:14 +0800 Subject: [PATCH 249/438] Use AbstractTestCase.log in ArrayUtilsTest Replace explicit Logger usage in ArrayUtilsTest with the inherited log(...) helper. Removed Logger imports and the private logger field, and replaced logger.isTraceEnabled()/logger.trace(...) blocks with single log(...) calls to simplify the tests and eliminate unused imports/boilerplate. --- .../io/microsphere/util/ArrayUtilsTest.java | 76 +++++-------------- 1 file changed, 18 insertions(+), 58 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java index 7fbf4918f..0e91ad1b0 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java @@ -17,7 +17,6 @@ package io.microsphere.util; import io.microsphere.AbstractTestCase; -import io.microsphere.logging.Logger; import org.junit.jupiter.api.Test; import java.io.File; @@ -30,7 +29,6 @@ import static io.microsphere.collection.EnumerationUtils.ofEnumeration; import static io.microsphere.collection.Lists.ofList; -import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.ArrayUtils.EMPTY_ANNOTATION_ARRAY; import static io.microsphere.util.ArrayUtils.EMPTY_BOOLEAN_ARRAY; import static io.microsphere.util.ArrayUtils.EMPTY_BOOLEAN_OBJECT_ARRAY; @@ -92,8 +90,6 @@ */ class ArrayUtilsTest extends AbstractTestCase { - private static final Logger logger = getLogger(ArrayUtilsTest.class); - @Test void testConstants() { @@ -909,9 +905,7 @@ void testCombine() { void testForEachWithConsumerOnBooleanArray() { boolean[] values = ofBooleans(true); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -919,9 +913,7 @@ void testForEachWithConsumerOnBooleanArray() { void testForEachWithConsumerOnByteArray() { byte[] values = ofBytes((byte) 1); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -929,9 +921,7 @@ void testForEachWithConsumerOnByteArray() { void testForEachWithConsumerOnCharArray() { char[] values = ofChars('A'); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -939,9 +929,7 @@ void testForEachWithConsumerOnCharArray() { void testForEachWithConsumerOnShortArray() { short[] values = ofShorts((short) 1); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -949,9 +937,7 @@ void testForEachWithConsumerOnShortArray() { void testForEachWithConsumerOnIntArray() { int[] values = ofInts(1); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -959,9 +945,7 @@ void testForEachWithConsumerOnIntArray() { void testForEachWithConsumerOnLongArray() { long[] values = ofLongs(1L); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -969,9 +953,7 @@ void testForEachWithConsumerOnLongArray() { void testForEachWithConsumerOnFloatArray() { float[] values = ofFloats(1F); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -979,9 +961,7 @@ void testForEachWithConsumerOnFloatArray() { void testForEachWithConsumerOnDoubleArray() { double[] values = ofDoubles(1D); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -989,9 +969,7 @@ void testForEachWithConsumerOnDoubleArray() { void testForEachWithConsumerOnObjectArray() { Object[] values = of("A"); forEach(values, (value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(value : {})", value); - } + log("forEach(value : {})", value); }); } @@ -999,9 +977,7 @@ void testForEachWithConsumerOnObjectArray() { void testForEachWithBiConsumerOnBooleanArray() { boolean[] values = ofBooleans(true); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } @@ -1009,9 +985,7 @@ void testForEachWithBiConsumerOnBooleanArray() { void testForEachWithBiConsumerOnByteArray() { byte[] values = ofBytes((byte) 1); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } @@ -1019,9 +993,7 @@ void testForEachWithBiConsumerOnByteArray() { void testForEachWithBiConsumerOnCharArray() { char[] values = ofChars('A'); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } @@ -1029,9 +1001,7 @@ void testForEachWithBiConsumerOnCharArray() { void testForEachWithBiConsumerOnShortArray() { short[] values = ofShorts((short) 1); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } @@ -1039,9 +1009,7 @@ void testForEachWithBiConsumerOnShortArray() { void testForEachWithBiConsumerOnIntArray() { int[] values = ofInts(1); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } @@ -1049,9 +1017,7 @@ void testForEachWithBiConsumerOnIntArray() { void testForEachWithBiConsumerOnLongArray() { long[] values = ofLongs(1L); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } @@ -1059,9 +1025,7 @@ void testForEachWithBiConsumerOnLongArray() { void testForEachWithBiConsumerOnFloatArray() { float[] values = ofFloats(1F); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } @@ -1069,9 +1033,7 @@ void testForEachWithBiConsumerOnFloatArray() { void testForEachWithBiConsumerOnDoubleArray() { double[] values = ofDoubles(1D); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } @@ -1079,9 +1041,7 @@ void testForEachWithBiConsumerOnDoubleArray() { void testForEachWithBiConsumerOnObjectArray() { Object[] values = of("A"); forEach(values, (index, value) -> { - if (logger.isTraceEnabled()) { - logger.trace("forEach(index : {} , value : {})", index, value); - } + log("forEach(index : {} , value : {})", index, value); }); } From e04cc40452d4115875a7da5bcd0068c8228e01af Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:38:25 +0800 Subject: [PATCH 250/438] Replace Logger with AbstractTestCase logging Extend ShutdownHookUtilsTest from AbstractTestCase and switch logging to the inherited log(...) method. Remove Logger import and static logger field/getLogger usage, updating logger.* calls to log(...). Change inner ShutdownHookCallback from static to instance class so it can use the instance logging. No functional test behavior changed. --- .../util/ShutdownHookUtilsTest.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java index 9421dd6fc..d9516148f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java @@ -16,15 +16,14 @@ */ package io.microsphere.util; +import io.microsphere.AbstractTestCase; import io.microsphere.lang.Prioritized; -import io.microsphere.logging.Logger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Queue; -import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.ShutdownHookUtils.SHUTDOWN_HOOK_CALLBACKS_THREAD_FILTER; import static io.microsphere.util.ShutdownHookUtils.addShutdownHookCallback; import static io.microsphere.util.ShutdownHookUtils.clearShutdownHookCallbacks; @@ -44,9 +43,7 @@ * @author Mercy * @since 1.0.0 */ -class ShutdownHookUtilsTest { - - private static final Logger logger = getLogger(ShutdownHookUtilsTest.class); +class ShutdownHookUtilsTest extends AbstractTestCase { @BeforeEach void setUp() { @@ -125,7 +122,7 @@ public void run() { Thread currentThread = Thread.currentThread(); synchronized (currentThread) { try { - logger.info("Thread[name : '{}'] is about to be waited...", currentThread.getName()); + log("Thread[name : '{}'] is about to be waited...", currentThread.getName()); currentThread.wait(); assertTrue(getShutdownHookCallbacks().isEmpty()); assertFalse(removeShutdownHookCallback(this)); @@ -140,7 +137,7 @@ public void run() { addShutdownHookCallback(() -> { synchronized (thread) { - logger.info("Thread[name : '{}'] is about to notify a waited Thread[name: '{}']...", Thread.currentThread().getName(), thread.getName()); + log("Thread[name : '{}'] is about to notify a waited Thread[name: '{}']...", Thread.currentThread().getName(), thread.getName()); thread.notify(); } }); @@ -150,7 +147,7 @@ public void run() { thread.start(); } - static class ShutdownHookCallback implements Runnable, Prioritized { + class ShutdownHookCallback implements Runnable, Prioritized { private final int priority; @@ -160,13 +157,12 @@ static class ShutdownHookCallback implements Runnable, Prioritized { @Override public void run() { - logger.trace("Run an instance of ShutdownHookCallback[priority : {}] : {}", priority, this); + log("Run an instance of ShutdownHookCallback[priority : {}] : {}", priority, this); } @Override public int getPriority() { return priority; } - } -} +} \ No newline at end of file From 310a5992352c06407df0afe1264c5980c880f7f2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 19:38:34 +0800 Subject: [PATCH 251/438] Extend StopWatchTest from AbstractTestCase StopWatchTest now extends AbstractTestCase to reuse test utilities and logging. Added import for AbstractTestCase, removed the static Logger field, and replaced logger.info(...) with the inherited log(...) call. No functional changes to the tests. --- .../src/test/java/io/microsphere/util/StopWatchTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java index b5504aa06..09a4832b3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java @@ -16,6 +16,7 @@ */ package io.microsphere.util; +import io.microsphere.AbstractTestCase; import io.microsphere.logging.Logger; import io.microsphere.util.StopWatch.Task; import org.junit.jupiter.api.BeforeEach; @@ -41,9 +42,7 @@ * @author Mercy * @since 1.0.0 */ -class StopWatchTest { - - private static final Logger logger = getLogger(StopWatchTest.class); +class StopWatchTest extends AbstractTestCase { private static final String testName = "test"; @@ -75,7 +74,7 @@ void test() throws InterruptedException { assertTrue(task.getElapsedNanos() > 0); assertTrue(stopWatch.getTotalTimeNanos() > 0); assertTrue(stopWatch.getTotalTime(MILLISECONDS) > 0); - logger.info(stopWatch.toString()); + log(stopWatch.toString()); } @Test From 8a142af7cb0a5cc7cc7169b1d3dcfe7399b110c3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 20:06:51 +0800 Subject: [PATCH 252/438] Add Loggable test base class Introduce an abstract Loggable class under test sources (io.microsphere) that provides a protected Logger (via LoggerFactory.getLogger) and three trace-guarded helper methods: log(Object), log(String, Object...), and log(String, Throwable). Intended as a test base/helper to simplify trace-level logging in tests. File includes the ASF license header. --- .../test/java/io/microsphere/Loggable.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/Loggable.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/Loggable.java b/microsphere-java-core/src/test/java/io/microsphere/Loggable.java new file mode 100644 index 000000000..13aa8c44b --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/Loggable.java @@ -0,0 +1,53 @@ +/* + * 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. + */ + +package io.microsphere; + +import io.microsphere.logging.Logger; + +import static io.microsphere.logging.LoggerFactory.getLogger; +import static java.lang.String.valueOf; + +/** + * Loggable + * + * @author Mercy + * @see Logger + * @since 1.0.0 + */ +public abstract class Loggable { + + protected final Logger logger = getLogger(getClass()); + + protected void log(Object object) { + if (logger.isTraceEnabled()) { + logger.trace(valueOf(object)); + } + } + + protected void log(String object, Object... args) { + if (logger.isTraceEnabled()) { + logger.trace(object, args); + } + } + + protected void log(String message, Throwable t) { + if (logger.isTraceEnabled()) { + logger.trace(message, t); + } + } +} From c9f0e04ee2f38ec8472075c5a6c8fb386ed45f82 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 20:06:59 +0800 Subject: [PATCH 253/438] Have AbstractTestCase extend Loggable Replace local Logger usage with a shared Loggable base: AbstractTestCase now extends Loggable and removes the private Logger field, getLogger/valueOf imports, and the local log(...) helper methods. Cleans up related unused imports; intended to centralize logging behavior with no other functional changes. --- .../java/io/microsphere/AbstractTestCase.java | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java index c9be0e306..108c9da99 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java +++ b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java @@ -4,7 +4,6 @@ package io.microsphere; import io.microsphere.lang.function.ThrowableAction; -import io.microsphere.logging.Logger; import io.microsphere.process.ProcessExecutor; import org.junit.jupiter.api.Disabled; @@ -28,13 +27,11 @@ import static io.microsphere.collection.QueueUtils.singletonDeque; import static io.microsphere.collection.QueueUtils.singletonQueue; import static io.microsphere.collection.SetUtils.newHashSet; -import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.management.JmxUtils.getRuntimeMXBean; import static io.microsphere.reflect.TypeUtils.asClass; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; import static io.microsphere.util.SystemUtils.IS_OS_WINDOWS; import static io.microsphere.util.SystemUtils.JAVA_IO_TMPDIR; -import static java.lang.String.valueOf; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static java.util.Collections.singleton; @@ -53,7 +50,7 @@ * @since 1.0.0 */ @Disabled -public abstract class AbstractTestCase { +public abstract class AbstractTestCase extends Loggable { public static final String TEST_ELEMENT = "test"; @@ -126,20 +123,6 @@ public abstract class AbstractTestCase { protected final ClassLoader classLoader = getClassLoader(getClass()); - protected final Logger logger = getLogger(getClass()); - - protected void log(Object object) { - if (logger.isTraceEnabled()) { - logger.trace(valueOf(object)); - } - } - - protected void log(String object, Object... args) { - if (logger.isTraceEnabled()) { - logger.trace(object, args); - } - } - protected File createRandomTempDirectory() { return createRandomDirectory(TEST_TEMP_DIR); } From 31bb2943e3fb311eabdc66dcf4b4d127ee75360b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 20:56:49 +0800 Subject: [PATCH 254/438] Add ShutdownHookCallback test class Introduce a new test helper ShutdownHookCallback in io.microsphere.util. The class implements Runnable and Prioritized, stores a priority, and logs when run. Includes ASF license header and author metadata and is intended to support tests around ShutdownHookUtils. --- .../util/ShutdownHookCallback.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallback.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallback.java b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallback.java new file mode 100644 index 000000000..f8305e561 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallback.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package io.microsphere.util; + +import io.microsphere.Loggable; +import io.microsphere.lang.Prioritized; + +/** + * The ShutdownHook Callback + * + * @author Mercy + * @see ShutdownHookUtils + * @since 1.0.0 + */ +class ShutdownHookCallback extends Loggable implements Runnable, Prioritized { + + private final int priority; + + ShutdownHookCallback(int priority) { + this.priority = priority; + } + + @Override + public void run() { + log("Run an instance of ShutdownHookCallback[priority : {}] : {}", priority, this); + } + + @Override + public int getPriority() { + return priority; + } +} From ebbd328f160628982b2d86cb3adeda1a51b62810 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 20:57:01 +0800 Subject: [PATCH 255/438] Instantiate outer test and simplify callback Instantiate ShutdownHookUtilsTest and replace qualified inner-class instantiation (ShutdownHookUtilsTest.ShutdownHookCallback) with a direct ShutdownHookCallback(...) call in the test. This simplifies the test code while keeping the existing behavior unchanged. --- .../io/microsphere/util/ShutdownHookCallbacksThreadTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallbacksThreadTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallbacksThreadTest.java index 918230cf7..26dfe3b1e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallbacksThreadTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallbacksThreadTest.java @@ -18,9 +18,10 @@ class ShutdownHookCallbacksThreadTest { void testRun() { ShutdownHookCallbacksThread thread = INSTANCE; + ShutdownHookUtilsTest test = new ShutdownHookUtilsTest(); int times = 3; for (int i = 0; i < times; i++) { - addShutdownHookCallback(new ShutdownHookUtilsTest.ShutdownHookCallback(i)); + addShutdownHookCallback(new ShutdownHookCallback(i)); } thread.run(); From 77f02827d27f096cee8f74276e6013f10bd52317 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 20:57:08 +0800 Subject: [PATCH 256/438] Switch test to Loggable and remove callback class Refactor ShutdownHookUtilsTest to extend Loggable instead of AbstractTestCase and update imports. Remove the Prioritized import and the inner ShutdownHookCallback class (a Runnable implementing Prioritized), since the test no longer requires the prioritized callback implementation. --- .../util/ShutdownHookUtilsTest.java | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java index d9516148f..89799e3cb 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java @@ -16,8 +16,7 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; -import io.microsphere.lang.Prioritized; +import io.microsphere.Loggable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -43,7 +42,7 @@ * @author Mercy * @since 1.0.0 */ -class ShutdownHookUtilsTest extends AbstractTestCase { +class ShutdownHookUtilsTest extends Loggable { @BeforeEach void setUp() { @@ -147,22 +146,4 @@ public void run() { thread.start(); } - class ShutdownHookCallback implements Runnable, Prioritized { - - private final int priority; - - ShutdownHookCallback(int priority) { - this.priority = priority; - } - - @Override - public void run() { - log("Run an instance of ShutdownHookCallback[priority : {}] : {}", priority, this); - } - - @Override - public int getPriority() { - return priority; - } - } } \ No newline at end of file From 4e3a786a1a1dd72e821f370ea143350644ab72f0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:03:07 +0800 Subject: [PATCH 257/438] Replace AbstractTestCase with Loggable Update BaseConverterTest to import Loggable and extend it instead of AbstractTestCase. This changes the test superclass (microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java) to rely on logging utilities rather than the previous test base, removing the dependency on AbstractTestCase. --- .../test/java/io/microsphere/convert/BaseConverterTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java index 59ba16972..08b5e08d9 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java @@ -18,7 +18,7 @@ package io.microsphere.convert; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,7 +36,7 @@ * @see AbstractConverter * @since 1.0.0 */ -abstract class BaseConverterTest extends AbstractTestCase { +abstract class BaseConverterTest extends Loggable { protected AbstractConverter converter; From 294402c17a68800ac4d8ab3d412835626977c41f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:03:48 +0800 Subject: [PATCH 258/438] Extend Loggable in AbstractEventListener Replace AbstractTestCase with Loggable as the superclass of AbstractEventListener and remove explicit Logger imports and factory usage. This simplifies the test listener by using the common Loggable base for logging support and removes unused test-case inheritance. --- .../java/io/microsphere/event/AbstractEventListener.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java b/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java index ade42fd8e..68de276a3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java @@ -16,14 +16,11 @@ */ package io.microsphere.event; -import io.microsphere.AbstractTestCase; -import io.microsphere.logging.Logger; +import io.microsphere.Loggable; import java.util.concurrent.atomic.AtomicInteger; -import static io.microsphere.logging.LoggerFactory.getLogger; - -public abstract class AbstractEventListener extends AbstractTestCase implements EventListener { +public abstract class AbstractEventListener extends Loggable implements EventListener { private final AtomicInteger eventOccurs = new AtomicInteger(0); From 0d77884073570d4ea38c881918f3261966352182 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:15:47 +0800 Subject: [PATCH 259/438] Add private log method to MethodDefinitionTest Introduce a private helper method log(String) in MethodDefinitionTest that delegates to super.log(message). This provides a local wrapper for calling the superclass logging method from within the test class. --- .../java/io/microsphere/reflect/MethodDefinitionTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java index 919867c1b..74da94744 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodDefinitionTest.java @@ -59,4 +59,8 @@ void testInvoke() { assertNull(definition.invoke(this, definition.toString())); } } + + private void log(String message) { + super.log(message); + } } From c85d30a69ad1630f039bd4e2d967187f26804b0a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:15:58 +0800 Subject: [PATCH 260/438] Use Loggable.logger in ModifierTest Replace import and field reference in ModifierTest: AbstractTestCase -> Loggable. Update the assertion to check the protected modifier on Loggable.logger instead of AbstractTestCase.logger to reflect the refactor moving the logger to Loggable. --- .../src/test/java/io/microsphere/reflect/ModifierTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java index c09542091..ecaa38576 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java @@ -16,7 +16,7 @@ */ package io.microsphere.reflect; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Test; import java.util.AbstractList; @@ -99,7 +99,7 @@ void testIsPrivate() throws NoSuchFieldException { @Test void testIsProtected() throws NoSuchFieldException { - assertTrue(isProtected(AbstractTestCase.class.getDeclaredField("logger").getModifiers())); + assertTrue(isProtected(Loggable.class.getDeclaredField("logger").getModifiers())); assertFalse(isProtected(String.class.getModifiers())); } From 25d4456209a5b27d3db29a501ff6081e169387fc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:16:10 +0800 Subject: [PATCH 261/438] Switch test base to Loggable Replace the AbstractTestCase import and superclass with Loggable in AbstractReflectiveDefinitionTest. This change updates the test class to use logging utilities provided by Loggable instead of the previous AbstractTestCase base. --- .../microsphere/reflect/AbstractReflectiveDefinitionTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java index cfc33edc2..56c1d0f64 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java @@ -1,6 +1,6 @@ package io.microsphere.reflect; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,7 +30,7 @@ * @see ReflectiveDefinition * @since 1.0.0 */ -public abstract class AbstractReflectiveDefinitionTest extends AbstractTestCase { +public abstract class AbstractReflectiveDefinitionTest extends Loggable { private final List[] headConstructorArgumentsArray = ofArray( ofList(SINCE, getClassName()), From 4a79bf50c4b87c4043da285c4cf6dd46df3016ed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:16:19 +0800 Subject: [PATCH 262/438] Make AbstractPerformanceTest extend Loggable Replace AbstractTestCase with Loggable in AbstractPerformanceTest: update import and change the class to extend io.microsphere.Loggable. This removes the dependency on AbstractTestCase for performance tests and switches the base to the Loggable utility. --- .../io/microsphere/performance/AbstractPerformanceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/performance/AbstractPerformanceTest.java b/microsphere-java-core/src/test/java/io/microsphere/performance/AbstractPerformanceTest.java index cdec0ce77..8a6dfdb49 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/performance/AbstractPerformanceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/performance/AbstractPerformanceTest.java @@ -1,6 +1,6 @@ package io.microsphere.performance; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Disabled; /** @@ -11,7 +11,7 @@ * @since 1.0.0 */ @Disabled -public abstract class AbstractPerformanceTest extends AbstractTestCase { +public abstract class AbstractPerformanceTest extends Loggable { protected void execute(PerformanceAction action) { long startTime = System.nanoTime(); From eb00da91837f13acc9c147c1a20c135bcb063a0c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:16:28 +0800 Subject: [PATCH 263/438] Add static import for TEST_CLASS_LOADER Introduce a static import of io.microsphere.AbstractTestCase.TEST_CLASS_LOADER in ClassLoaderUtilsPerformanceTest to allow direct use of the predefined test class loader. Improves readability and consistency when referencing the test class loader in performance tests. --- .../io/microsphere/util/ClassLoaderUtilsPerformanceTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsPerformanceTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsPerformanceTest.java index 404e9a9fe..ee04c9176 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsPerformanceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsPerformanceTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import static io.microsphere.AbstractTestCase.TEST_CLASS_LOADER; import static io.microsphere.util.ClassLoaderUtils.findLoadedClassesInClassPath; /** From c88f8873882190c75e55ed7d44c30b403900bf10 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:20:50 +0800 Subject: [PATCH 264/438] Remove AbstractTestCase from VersionUtilsTest Decouple the test from the AbstractTestCase base class: remove the import and class inheritance, and add a static import for TEST_NULL_STRING. This makes the test independent of the test base while keeping access to the shared TEST_NULL_STRING constant. --- .../src/test/java/io/microsphere/util/VersionUtilsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/VersionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/VersionUtilsTest.java index 03db75f00..8f1d556cd 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/VersionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/VersionUtilsTest.java @@ -16,9 +16,9 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; +import static io.microsphere.AbstractTestCase.TEST_NULL_STRING; import static io.microsphere.util.StringUtils.substringAfter; import static io.microsphere.util.Version.Operator.of; import static io.microsphere.util.Version.ofVersion; @@ -56,7 +56,7 @@ * @see VersionUtils * @since 1.0.0 */ -class VersionUtilsTest extends AbstractTestCase { +class VersionUtilsTest { private static final String TEST_VERSION_STRING_1_1_0 = "1.1.0"; From 4b5d83fc163d157e1abbeb69671368c8d5623fed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:20:57 +0800 Subject: [PATCH 265/438] Remove AbstractTestCase inheritance from VersionTest Delete the unused AbstractTestCase import and stop extending it in VersionTest. Cleans up the test class so it is a standalone JUnit test without relying on the project base test class. --- .../src/test/java/io/microsphere/util/VersionTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/VersionTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/VersionTest.java index d044a13a0..3cce37b6d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/VersionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/VersionTest.java @@ -16,7 +16,6 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; import static io.microsphere.constants.SymbolConstants.DOT; @@ -38,7 +37,7 @@ * @see Version * @since 1.0.0 */ -class VersionTest extends AbstractTestCase { +class VersionTest { private static final int MAJOR = 1; From 269a252177a62e23df900a4daf2efd80bb7353bd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:21:06 +0800 Subject: [PATCH 266/438] Replace AbstractTestCase inheritance with static imports Remove extends AbstractTestCase from AnnotationUtilsTest and delete the direct AbstractTestCase import. Add static imports for test constants (e.g. TEST_NULL_LIST, TEST_EMPTY_LIST, TEST_NULL_ANNOTATION, etc.) so the test uses those values without inheriting the base test class. This decouples the test class from the test base while preserving existing constants usage. --- .../java/io/microsphere/util/AnnotationUtilsTest.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java index d38b0f1a8..53ddb9f0a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/AnnotationUtilsTest.java @@ -16,7 +16,6 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; import io.microsphere.annotation.Since; import org.junit.jupiter.api.Test; @@ -34,6 +33,14 @@ import java.util.List; import java.util.Map; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_ANNOTATION_CLASSES; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_LIST; +import static io.microsphere.AbstractTestCase.TEST_NULL_ANNOTATION; +import static io.microsphere.AbstractTestCase.TEST_NULL_ANNOTATION_CLASS; +import static io.microsphere.AbstractTestCase.TEST_NULL_ANNOTATION_CLASSES; +import static io.microsphere.AbstractTestCase.TEST_NULL_COLLECTION; +import static io.microsphere.AbstractTestCase.TEST_NULL_ITERABLE; +import static io.microsphere.AbstractTestCase.TEST_NULL_LIST; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.ofMap; import static io.microsphere.reflect.MethodUtils.OBJECT_PUBLIC_METHODS; @@ -92,7 +99,7 @@ * @author Mercy * @since 1.0.0 */ -class AnnotationUtilsTest extends AbstractTestCase { +class AnnotationUtilsTest { private static final Method stringEqualsMethod = findMethod(String.class, "equals", Object.class); From 142679701ee93c1e61ce41450eb51ff6a021cfe9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:21:15 +0800 Subject: [PATCH 267/438] Use Loggable as base for ReadOnlyIteratorTest Replace AbstractTestCase with Loggable as the superclass for ReadOnlyIteratorTest and adjust imports accordingly. Add a static import for TEST_ELEMENT from AbstractTestCase so the test can still use the shared test element constant while decoupling the test from AbstractTestCase. --- .../java/io/microsphere/collection/ReadOnlyIteratorTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ReadOnlyIteratorTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ReadOnlyIteratorTest.java index 24b0dce86..260a1c33a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ReadOnlyIteratorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ReadOnlyIteratorTest.java @@ -16,12 +16,13 @@ */ package io.microsphere.collection; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Test; import java.util.Iterator; import java.util.NoSuchElementException; +import static io.microsphere.AbstractTestCase.TEST_ELEMENT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -33,7 +34,7 @@ * @see ReadOnlyIterator * @since 1.0.0 */ -public abstract class ReadOnlyIteratorTest extends AbstractTestCase { +public abstract class ReadOnlyIteratorTest extends Loggable { Iterator instance = createIterator(); From fbd534465515ffb616a13bb4a5549810b4037cc0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:22:15 +0800 Subject: [PATCH 268/438] Replace AbstractTestCase with Loggable Update ArrayUtilsTest to use Loggable instead of AbstractTestCase and add a static import for TEST_NULL_OBJECT_ARRAY. This changes the imported test utilities and the test class base, enabling use of the TEST_NULL_OBJECT_ARRAY constant in the test file. --- .../src/test/java/io/microsphere/util/ArrayUtilsTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java index 0e91ad1b0..580aca9ed 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java @@ -16,7 +16,7 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Test; import java.io.File; @@ -27,6 +27,7 @@ import java.util.Enumeration; import java.util.List; +import static io.microsphere.AbstractTestCase.TEST_NULL_OBJECT_ARRAY; import static io.microsphere.collection.EnumerationUtils.ofEnumeration; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.util.ArrayUtils.EMPTY_ANNOTATION_ARRAY; @@ -88,7 +89,7 @@ * @author Mercy * @since 1.0.0 */ -class ArrayUtilsTest extends AbstractTestCase { +class ArrayUtilsTest extends Loggable { @Test void testConstants() { @@ -1148,4 +1149,4 @@ private void assertEmptyArray(E[] array, Class expectedComponentType) { assertEquals(0, array.length); assertEquals(expectedComponentType, getTopComponentType(array)); } -} +} \ No newline at end of file From 452eda46d4dddde87b9851b8b48b535806bb101c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:23:44 +0800 Subject: [PATCH 269/438] Detach ArtifactTest from AbstractTestCase Remove the AbstractTestCase import and stop extending it in ArtifactTest. Instead, add a static import for TEST_CLASS_LOADER from AbstractTestCase so the test can use the shared class loader without inheriting the test base class. --- .../test/java/io/microsphere/classloading/ArtifactTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactTest.java index df7e65280..03c6f6baf 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactTest.java @@ -16,12 +16,12 @@ */ package io.microsphere.classloading; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.net.URL; +import static io.microsphere.AbstractTestCase.TEST_CLASS_LOADER; import static io.microsphere.classloading.Artifact.UNKNOWN; import static io.microsphere.classloading.Artifact.WILDCARD; import static io.microsphere.classloading.Artifact.create; @@ -40,7 +40,7 @@ * @see Artifact * @since 1.0.0 */ -class ArtifactTest extends AbstractTestCase { +class ArtifactTest { private static final String ARTIFACT_ID = "microsphere-core"; From 507d80895f5e0d9e72e28e5190aae5c9868c8d7e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:23:52 +0800 Subject: [PATCH 270/438] Remove AbstractTestCase inheritance from AssertTest Replace the AssertTest class's inheritance from AbstractTestCase with static imports of required test constants (TEST_NULL_OBJECT_ARRAY, TEST_NULL_STRING). Updated imports and removed the extends clause; no other logic changes to the test file. --- .../src/test/java/io/microsphere/util/AssertTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/AssertTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/AssertTest.java index a6de60b2b..510262ab9 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/AssertTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/AssertTest.java @@ -16,13 +16,14 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.Map; import java.util.function.Supplier; +import static io.microsphere.AbstractTestCase.TEST_NULL_OBJECT_ARRAY; +import static io.microsphere.AbstractTestCase.TEST_NULL_STRING; import static io.microsphere.collection.MapUtils.ofMap; import static io.microsphere.constants.SymbolConstants.SPACE; import static io.microsphere.util.Assert.assertArrayIndex; @@ -48,7 +49,7 @@ * @see Assert * @since 1.0.0 */ -class AssertTest extends AbstractTestCase { +class AssertTest { @Test void testAssertTrue() { From d905657927803f48c583f62b25e98b8eb37bfc21 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:27:41 +0800 Subject: [PATCH 271/438] Make test file utilities static and rename helper Convert various file utility methods in AbstractTestCase from protected instance methods to public static methods to allow reuse without instantiating the test class. Also rename buildRandomFileName() to randomFileName(). Affected helpers: createRandomTempDirectory, createRandomTempFile, newRandomTempFile, createRandomDirectory(File), createRandomFile(File), newRandomFile(File) and randomFileName(), plus makeLinkFile(File). No behavioral changes beyond visibility/static and the one method rename. --- .../java/io/microsphere/AbstractTestCase.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java index 108c9da99..393fee5b7 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java +++ b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java @@ -123,41 +123,41 @@ public abstract class AbstractTestCase extends Loggable { protected final ClassLoader classLoader = getClassLoader(getClass()); - protected File createRandomTempDirectory() { + public static File createRandomTempDirectory() { return createRandomDirectory(TEST_TEMP_DIR); } - protected File createRandomTempFile() throws IOException { + public static File createRandomTempFile() throws IOException { return createRandomFile(TEST_TEMP_DIR); } - protected File newRandomTempFile() { + public static File newRandomTempFile() { return newRandomFile(TEST_TEMP_DIR); } - protected File createRandomDirectory(File parentDir) { + public static File createRandomDirectory(File parentDir) { File tempDir = newRandomFile(parentDir); assertTrue(tempDir.mkdir()); tempDir.deleteOnExit(); return tempDir; } - protected File createRandomFile(File parentDir) throws IOException { + public static File createRandomFile(File parentDir) throws IOException { File randomFile = newRandomFile(parentDir); assertTrue(randomFile.createNewFile()); randomFile.deleteOnExit(); return randomFile; } - protected File newRandomFile(File parentDir) { - return new File(parentDir, buildRandomFileName()); + public static File newRandomFile(File parentDir) { + return new File(parentDir, randomFileName()); } - protected String buildRandomFileName() { + public static String randomFileName() { return randomUUID().toString(); } - protected File makeLinkFile(File targetFile) throws Exception { + public static File makeLinkFile(File targetFile) throws Exception { File tempDir = createRandomTempDirectory(); File linkFile = new File(tempDir, "link"); boolean directory = targetFile.isDirectory(); From 40744a516c86c754e76d5632faf414119f0e7dfb Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:27:51 +0800 Subject: [PATCH 272/438] Remove AbstractTestCase from BeanUtilsTest Remove the AbstractTestCase inheritance and its import from BeanUtilsTest. Add a static import for newRandomTempFile and update the class to no longer extend the test base, simplifying the test setup and relying on the utility method directly. --- .../src/test/java/io/microsphere/beans/BeanUtilsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java index a978f1b6f..811efb821 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java @@ -18,7 +18,6 @@ package io.microsphere.beans; -import io.microsphere.AbstractTestCase; import io.microsphere.io.event.FileChangedEvent; import io.microsphere.test.MultipleValueData; import org.junit.jupiter.api.Test; @@ -33,6 +32,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import static io.microsphere.AbstractTestCase.newRandomTempFile; import static io.microsphere.beans.BeanUtils.findPropertyDescriptor; import static io.microsphere.beans.BeanUtils.findWriteMethod; import static io.microsphere.beans.BeanUtils.getBeanMetadata; @@ -62,7 +62,7 @@ * @see BeanUtils * @since 1.0.0 */ -class BeanUtilsTest extends AbstractTestCase { +class BeanUtilsTest { @Test void testGetBeanMetadata() { From b7d5a7d6d31e255cdb2159d728106d85e3a03de8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:28:34 +0800 Subject: [PATCH 273/438] Remove AbstractTestCase from test class Delete the unused AbstractTestCase import and drop the extends AbstractTestCase from ClassDataRepositoryTest. The test no longer depends on the base test class, simplifying the test setup and removing an unnecessary inheritance. --- .../test/java/io/microsphere/lang/ClassDataRepositoryTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/ClassDataRepositoryTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/ClassDataRepositoryTest.java index be40025a9..5d43226a0 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/ClassDataRepositoryTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/ClassDataRepositoryTest.java @@ -16,7 +16,6 @@ */ package io.microsphere.lang; -import io.microsphere.AbstractTestCase; import io.microsphere.reflect.ReflectionUtils; import org.junit.jupiter.api.Test; @@ -38,7 +37,7 @@ * @see ClassDataRepository * @since 1.0.0 */ -class ClassDataRepositoryTest extends AbstractTestCase { +class ClassDataRepositoryTest { private final ClassDataRepository repository = INSTANCE; From a04a4a5b2850172dfa8d6592a7b0223b7318405b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:30:50 +0800 Subject: [PATCH 274/438] Import TEST_ELEMENT in SingletonEnumerationTest Add a static import for io.microsphere.AbstractTestCase.TEST_ELEMENT in SingletonEnumerationTest so the test can reference the shared TEST_ELEMENT constant without qualification. --- .../java/io/microsphere/collection/SingletonEnumerationTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/SingletonEnumerationTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/SingletonEnumerationTest.java index 37f295e2c..51a0808e4 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/SingletonEnumerationTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/SingletonEnumerationTest.java @@ -18,6 +18,7 @@ import java.util.Iterator; +import static io.microsphere.AbstractTestCase.TEST_ELEMENT; import static io.microsphere.collection.CollectionUtils.singletonEnumeration; import static io.microsphere.collection.CollectionUtils.toIterator; From 74322bcf468688355efea3174911226884cb9195 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:30:58 +0800 Subject: [PATCH 275/438] Import TEST_ELEMENT in SingletonIteratorTest Add a static import for AbstractTestCase.TEST_ELEMENT in SingletonIteratorTest so the test can reference the shared test element constant directly. --- .../java/io/microsphere/collection/SingletonIteratorTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/SingletonIteratorTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/SingletonIteratorTest.java index 0f148bf7d..0db2d39f0 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/SingletonIteratorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/SingletonIteratorTest.java @@ -18,6 +18,7 @@ import java.util.Iterator; +import static io.microsphere.AbstractTestCase.TEST_ELEMENT; import static io.microsphere.collection.CollectionUtils.singletonIterator; /** From c4b6cbc14f8e7e01b63fb3f3268c97ee098e2167 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:31:05 +0800 Subject: [PATCH 276/438] Import TEST_ELEMENT in UnmodifiableIteratorTest Add a static import for io.microsphere.AbstractTestCase.TEST_ELEMENT in UnmodifiableIteratorTest.java to use the shared test constant directly, improving readability and avoiding fully-qualified references in the test. --- .../java/io/microsphere/collection/UnmodifiableIteratorTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/UnmodifiableIteratorTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/UnmodifiableIteratorTest.java index 079e81624..0f2fb6237 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/UnmodifiableIteratorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/UnmodifiableIteratorTest.java @@ -18,6 +18,7 @@ import java.util.Iterator; +import static io.microsphere.AbstractTestCase.TEST_ELEMENT; import static io.microsphere.collection.CollectionUtils.singletonIterator; import static io.microsphere.collection.CollectionUtils.unmodifiableIterator; From 68d855148c60b4bd21622e4ca070a847bd8505fa Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:31:15 +0800 Subject: [PATCH 277/438] Switch ClassPathUtilsTest to Loggable Replace AbstractTestCase as the test base with Loggable in ClassPathUtilsTest. Add a static import for TEST_CLASS_LOADER from AbstractTestCase and update imports accordingly. This is a refactor of test scaffolding and does not change test logic. --- .../test/java/io/microsphere/util/ClassPathUtilsTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java index bdfc5c3a1..66e449a72 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java @@ -3,7 +3,7 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Test; import java.lang.management.RuntimeMXBean; @@ -11,6 +11,7 @@ import java.util.Set; import java.util.function.Predicate; +import static io.microsphere.AbstractTestCase.TEST_CLASS_LOADER; import static io.microsphere.lang.ClassDataRepository.INSTANCE; import static io.microsphere.util.ClassLoaderUtils.isLoadedClass; import static io.microsphere.util.ClassPathUtils.getBootstrapClassPaths; @@ -29,7 +30,7 @@ * @see ClassPathUtilsTest * @since 1.0.0 */ -class ClassPathUtilsTest extends AbstractTestCase { +class ClassPathUtilsTest extends Loggable { @Test void testGetBootstrapClassPaths() { From 9f9e2577e7809b2f8b2e67b110f66124cbbc8cc3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:32:37 +0800 Subject: [PATCH 278/438] Remove AbstractTestCase from ClassUtilsTest Stop extending AbstractTestCase in ClassUtilsTest and remove its import. Add static imports for createRandomTempFile and makeLinkFile so tests can call those helpers without subclassing. Minor trailing newline cleanup. --- .../src/test/java/io/microsphere/util/ClassUtilsTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java index 9174d67d4..185715140 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java @@ -3,7 +3,6 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; import io.microsphere.lang.ClassDataRepository; import io.microsphere.test.A; import org.junit.jupiter.api.Test; @@ -30,6 +29,8 @@ import java.util.function.BiFunction; import java.util.function.Predicate; +import static io.microsphere.AbstractTestCase.createRandomTempFile; +import static io.microsphere.AbstractTestCase.makeLinkFile; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.ofEntry; import static io.microsphere.constants.SymbolConstants.SPACE; @@ -109,7 +110,7 @@ * @see ClassUtilsTest * @since 1.0.0 */ -class ClassUtilsTest extends AbstractTestCase { +class ClassUtilsTest { @Test void testConstants() { @@ -921,5 +922,4 @@ private void assertFindClassNamesInClassPath(URL location) { Set classNamesInClassPath = findClassNamesInClassPath(path, true); assertFalse(classNamesInClassPath.isEmpty()); } -} - +} \ No newline at end of file From 75cc6408c272fcf0292b33c9a6668b5c6f8be858 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:35:22 +0800 Subject: [PATCH 279/438] Use Loggable and constants in CollectionUtilsTest Replace inheritance from AbstractTestCase with Loggable in CollectionUtilsTest and add static imports for common test constants (TEST_ELEMENT, TEST_EMPTY_*, TEST_NULL_*, TEST_SINGLETON_*) from AbstractTestCase to simplify references. Adjust imports accordingly and tidy file ending. --- .../collection/CollectionUtilsTest.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/CollectionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/CollectionUtilsTest.java index 5686fba24..7a2aef4f5 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/CollectionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/CollectionUtilsTest.java @@ -16,7 +16,7 @@ */ package io.microsphere.collection; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Test; import java.util.Collections; @@ -25,6 +25,24 @@ import java.util.NoSuchElementException; import java.util.Set; +import static io.microsphere.AbstractTestCase.TEST_ELEMENT; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_COLLECTION; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_DEQUE; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_LIST; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_QUEUE; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_SET; +import static io.microsphere.AbstractTestCase.TEST_NULL_COLLECTION; +import static io.microsphere.AbstractTestCase.TEST_NULL_DEQUE; +import static io.microsphere.AbstractTestCase.TEST_NULL_ENUMERATION; +import static io.microsphere.AbstractTestCase.TEST_NULL_ITERABLE; +import static io.microsphere.AbstractTestCase.TEST_NULL_ITERATOR; +import static io.microsphere.AbstractTestCase.TEST_NULL_LIST; +import static io.microsphere.AbstractTestCase.TEST_NULL_QUEUE; +import static io.microsphere.AbstractTestCase.TEST_NULL_SET; +import static io.microsphere.AbstractTestCase.TEST_SINGLETON_DEQUE; +import static io.microsphere.AbstractTestCase.TEST_SINGLETON_LIST; +import static io.microsphere.AbstractTestCase.TEST_SINGLETON_QUEUE; +import static io.microsphere.AbstractTestCase.TEST_SINGLETON_SET; import static io.microsphere.collection.CollectionUtils.addAll; import static io.microsphere.collection.CollectionUtils.emptyDeque; import static io.microsphere.collection.CollectionUtils.emptyIterable; @@ -56,7 +74,7 @@ * @author Mercy * @since 1.0.0 */ -class CollectionUtilsTest extends AbstractTestCase { +class CollectionUtilsTest extends Loggable { @Test void testIsEmpty() { @@ -260,5 +278,4 @@ void testEmptyDeque() { assertSame(QueueUtils.emptyDeque(), emptyDeque()); assertSame(EMPTY_DEQUE, emptyDeque()); } - -} +} \ No newline at end of file From 690cfb3a1ef10ec8aceb488723f768d85f2ee086 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:35:50 +0800 Subject: [PATCH 280/438] Use Loggable base class in test Replace AbstractTestCase with Loggable in DelegatingScheduledExecutorServiceTest. Update the import and superclass to use Loggable, aligning the test with the refactored/shared logging test base. --- .../concurrent/DelegatingScheduledExecutorServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java index 8c7e6ca9f..87d369b78 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java @@ -1,6 +1,6 @@ package io.microsphere.concurrent; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Test; import java.util.List; @@ -23,7 +23,7 @@ * @see DelegatingScheduledExecutorService * @since 1.0.0 */ -class DelegatingScheduledExecutorServiceTest extends AbstractTestCase { +class DelegatingScheduledExecutorServiceTest extends Loggable { private ScheduledExecutorService delegate = newSingleThreadScheduledExecutor(); From 44f231fe54a79dfc6e8b3afc88b2a7bae45a25b4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:37:56 +0800 Subject: [PATCH 281/438] Test EnumerationIteratorAdapter with null enumeration Simplify EnumerationIteratorAdapterTest by removing dependence on AbstractTestCase and collection test utilities. The test now constructs an EnumerationIteratorAdapter with a null enumeration and asserts that hasNext() is false, and adds the necessary JUnit assertion import. --- .../collection/EnumerationIteratorAdapterTest.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/EnumerationIteratorAdapterTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/EnumerationIteratorAdapterTest.java index a4c7914c9..555a7e821 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/EnumerationIteratorAdapterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/EnumerationIteratorAdapterTest.java @@ -16,12 +16,9 @@ */ package io.microsphere.collection; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; -import java.util.Iterator; - -import static io.microsphere.collection.CollectionUtils.singletonIterator; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * {@link EnumerationIteratorAdapter} Test @@ -29,11 +26,11 @@ * @author Mercy * @since 1.0.0 */ -class EnumerationIteratorAdapterTest extends AbstractTestCase { +class EnumerationIteratorAdapterTest { @Test void test() { - Iterator iterator = singletonIterator(TEST_ELEMENT); - + EnumerationIteratorAdapter adapter = new EnumerationIteratorAdapter(null); + assertFalse(adapter.hasNext()); } } From ac1b4cdca39997d129bb463b77a2aaaec79df2ff Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:38:34 +0800 Subject: [PATCH 282/438] Replace AbstractTestCase with Loggable in test Update ExecutorUtilsTest to use Loggable as the base test class: change import from io.microsphere.AbstractTestCase to io.microsphere.Loggable and update the class declaration to extend Loggable. No other logic changes were made. --- .../java/io/microsphere/concurrent/ExecutorUtilsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java index 26d821203..4babd9e25 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java @@ -1,6 +1,6 @@ package io.microsphere.concurrent; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import io.microsphere.util.ShutdownHookUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -28,7 +28,7 @@ * @see ExecutorUtils * @since 1.0.0 */ -class ExecutorUtilsTest extends AbstractTestCase { +class ExecutorUtilsTest extends Loggable { private ExecutorService executorService; From 71db35e5d6e1ca3f8db389bc02ff3645b79a3aea Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:38:58 +0800 Subject: [PATCH 283/438] Use Loggable and createRandomTempFile in test Replace AbstractTestCase inheritance with Loggable in FileChangedListenerTest and add a static import for createRandomTempFile. Adjust imports accordingly to decouple the test from AbstractTestCase and use the shared utility for creating temporary files. --- .../io/microsphere/io/event/FileChangedListenerTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/event/FileChangedListenerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/event/FileChangedListenerTest.java index e8ca58e82..7bfbfd32a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/event/FileChangedListenerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/event/FileChangedListenerTest.java @@ -1,6 +1,6 @@ package io.microsphere.io.event; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import io.microsphere.io.event.FileChangedEvent.Kind; import io.microsphere.util.ValueHolder; import org.junit.jupiter.api.AfterEach; @@ -10,6 +10,7 @@ import java.io.File; import java.io.IOException; +import static io.microsphere.AbstractTestCase.createRandomTempFile; import static io.microsphere.io.FileUtils.forceDelete; import static io.microsphere.io.event.FileChangedEvent.Kind.CREATED; import static io.microsphere.io.event.FileChangedEvent.Kind.DELETED; @@ -27,7 +28,7 @@ * @see FileChangedListener * @since */ -class FileChangedListenerTest extends AbstractTestCase { +class FileChangedListenerTest extends Loggable { private FileChangedListener listener; From f61b0ab56c780f3e2a638662bd69857e1acd98d9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:40:05 +0800 Subject: [PATCH 284/438] Use Loggable and static-import AbstractTestCase Replace direct AbstractTestCase inheritance in FileUtilsTest with Loggable and remove the class import. Add static imports for TEST_CLASS_LOADER and several helper methods from AbstractTestCase (createRandomDirectory, createRandomFile, createRandomTempDirectory, createRandomTempFile, makeLinkFile, newRandomTempFile, random) so tests continue to use the utilities without extending the base test class. Updates only affect microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java. --- .../test/java/io/microsphere/io/FileUtilsTest.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index b0c8361e3..0649d43bb 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -1,6 +1,6 @@ package io.microsphere.io; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import io.microsphere.lang.function.ThrowableConsumer; import org.junit.jupiter.api.Test; @@ -13,6 +13,14 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import static io.microsphere.AbstractTestCase.TEST_CLASS_LOADER; +import static io.microsphere.AbstractTestCase.createRandomDirectory; +import static io.microsphere.AbstractTestCase.createRandomFile; +import static io.microsphere.AbstractTestCase.createRandomTempDirectory; +import static io.microsphere.AbstractTestCase.createRandomTempFile; +import static io.microsphere.AbstractTestCase.makeLinkFile; +import static io.microsphere.AbstractTestCase.newRandomTempFile; +import static io.microsphere.AbstractTestCase.random; import static io.microsphere.concurrent.CustomizedThreadFactory.newThreadFactory; import static io.microsphere.concurrent.ExecutorUtils.shutdown; import static io.microsphere.constants.FileConstants.CLASS; @@ -52,7 +60,7 @@ * @see FileUtils * @since 1.0.0 */ -class FileUtilsTest extends AbstractTestCase { +class FileUtilsTest extends Loggable { private final URL classFileResource = getClassResource(TEST_CLASS_LOADER, FileUtilsTest.class); From e5f3c8291896628bc892c460ebbe8974076f8b93 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:40:50 +0800 Subject: [PATCH 285/438] Use Loggable base in FileWatchServiceTest Replace AbstractTestCase with Loggable as the test base to leverage logging utilities, and add a static import for newRandomTempFile for test file creation. No functional changes to the test behavior; this only updates the base class and imports for clarity and utility. --- .../test/java/io/microsphere/io/FileWatchServiceTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java index 1e1e2aea2..ca8ece861 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java @@ -1,11 +1,12 @@ package io.microsphere.io; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import io.microsphere.io.event.LoggingFileChangedListener; import org.junit.jupiter.api.Test; import java.io.File; +import static io.microsphere.AbstractTestCase.newRandomTempFile; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.io.event.FileChangedEvent.Kind.values; @@ -16,7 +17,7 @@ * @see FileWatchService * @since 1.0.0 */ -class FileWatchServiceTest extends AbstractTestCase { +class FileWatchServiceTest extends Loggable { private FileWatchService service = (file, listener, kinds) -> { log("Watching : {} , listener : {} , kinds : {}", file, listener, kinds); From 68e21f4b0cf8b599bf013cba83cf159683a9ecac Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:41:46 +0800 Subject: [PATCH 286/438] Refactor FilterUtilsTest to use lambdas & statics Replace anonymous Filter implementations with lambda expressions and add static imports for FilterUtils.filter and FilterOperator.AND/OR. Remove the unused AbstractTestCase import/extension and simplify test calls to make the test more concise and idiomatic. --- .../microsphere/filter/FilterUtilsTest.java | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/filter/FilterUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/filter/FilterUtilsTest.java index 5ed936043..f0cf9cfc0 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/filter/FilterUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/filter/FilterUtilsTest.java @@ -3,11 +3,13 @@ */ package io.microsphere.filter; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; import java.util.List; +import static io.microsphere.filter.FilterOperator.AND; +import static io.microsphere.filter.FilterOperator.OR; +import static io.microsphere.filter.FilterUtils.filter; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -18,37 +20,22 @@ * @see FilterUtilsTest * @since 1.0.0 */ -class FilterUtilsTest extends AbstractTestCase { +class FilterUtilsTest { @Test void testFilter() { List integerList = asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - List result = FilterUtils.filter(integerList, FilterOperator.AND, new Filter() { - @Override - public boolean accept(Integer filteredObject) { - return filteredObject % 2 == 0; - } - }); + List result = filter(integerList, AND, filteredObject -> filteredObject % 2 == 0); assertEquals(asList(0, 2, 4, 6, 8), result); - result = FilterUtils.filter(integerList, new Filter() { - @Override - public boolean accept(Integer filteredObject) { - return filteredObject % 2 == 0; - } - }); + result = filter(integerList, filteredObject -> filteredObject % 2 == 0); assertEquals(asList(0, 2, 4, 6, 8), result); - result = FilterUtils.filter(integerList, FilterOperator.OR, new Filter() { - @Override - public boolean accept(Integer filteredObject) { - return filteredObject % 2 == 1; - } - }); + result = filter(integerList, OR, filteredObject -> filteredObject % 2 == 1); assertEquals(asList(1, 3, 5, 7, 9), result); } -} +} \ No newline at end of file From 374d3c5a15000b2a69c0785e822aa881f3205bd8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:42:25 +0800 Subject: [PATCH 287/438] Remove AbstractTestCase inheritance in IOUtilsTest Decouple IOUtilsTest from the AbstractTestCase base by removing the extends clause and the direct import. The TEST_NULL_STRING constant is kept via a static import, cleaning up an unused dependency while preserving test behavior. --- .../src/test/java/io/microsphere/io/IOUtilsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/IOUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/IOUtilsTest.java index 2bfab4a32..0f5994c10 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/IOUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/IOUtilsTest.java @@ -1,6 +1,5 @@ package io.microsphere.io; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -11,6 +10,7 @@ import java.io.StringReader; import java.nio.charset.Charset; +import static io.microsphere.AbstractTestCase.TEST_NULL_STRING; import static io.microsphere.io.IOUtils.BUFFER_SIZE; import static io.microsphere.io.IOUtils.DEFAULT_BUFFER_SIZE; import static io.microsphere.io.IOUtils.close; @@ -31,7 +31,7 @@ * @see IOUtils * @since 1.0.0 */ -class IOUtilsTest extends AbstractTestCase { +class IOUtilsTest { private static final String TEST_VALUE = "Hello"; From 2e4b6e27ee0b69033efd7a4a6870570a277c2408 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:43:18 +0800 Subject: [PATCH 288/438] Remove AbstractTestCase inheritance in test Simplify IterableAdapterTest by removing its inheritance from AbstractTestCase and instead statically importing TEST_ELEMENT. Cleans up imports and reduces coupling to the base test class; minor file cleanup (EOF newline change). --- .../java/io/microsphere/collection/IterableAdapterTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/IterableAdapterTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/IterableAdapterTest.java index e74078e64..f11acfeec 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/IterableAdapterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/IterableAdapterTest.java @@ -16,11 +16,11 @@ */ package io.microsphere.collection; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; import java.util.Iterator; +import static io.microsphere.AbstractTestCase.TEST_ELEMENT; import static io.microsphere.collection.CollectionUtils.singletonIterator; import static io.microsphere.collection.CollectionUtils.toIterable; import static io.microsphere.collection.EmptyIterator.INSTANCE; @@ -32,7 +32,7 @@ * @author Mercy * @since 1.0.0 */ -class IterableAdapterTest extends AbstractTestCase { +class IterableAdapterTest { @Test void test() { @@ -42,4 +42,4 @@ void test() { assertSame(INSTANCE, new IterableAdapter(null).iterator()); } -} +} \ No newline at end of file From 706481fabfaeaf79b4aa1dc94fe4eea090bf266b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:43:57 +0800 Subject: [PATCH 289/438] Remove AbstractTestCase base class from JSONTest Stop extending AbstractTestCase in JSONTest and switch to a static import of AbstractTestCase.random. Adjust imports and class declaration so the test class is a plain class while still using the random helper method. No test logic changed. --- .../src/test/java/io/microsphere/json/JSONTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/json/JSONTest.java b/microsphere-java-core/src/test/java/io/microsphere/json/JSONTest.java index 5585f297b..bec552aaf 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/json/JSONTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/json/JSONTest.java @@ -18,9 +18,9 @@ package io.microsphere.json; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; +import static io.microsphere.AbstractTestCase.random; import static io.microsphere.json.JSON.checkDouble; import static io.microsphere.json.JSON.toBoolean; import static io.microsphere.json.JSON.toDouble; @@ -42,7 +42,7 @@ * @see JSON * @since 1.0.0 */ -class JSONTest extends AbstractTestCase { +class JSONTest { @Test void testCheckDouble() throws Throwable { From 9443ffc1e92a837f8a51aa6e506bf7a7a707e8cd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:44:51 +0800 Subject: [PATCH 290/438] Remove AbstractTestCase inheritance in JarUtilsTest Decouple JarUtilsTest from the AbstractTestCase base class: remove the direct import of io.microsphere.AbstractTestCase and the 'extends AbstractTestCase' declaration, and add static imports for createRandomTempDirectory and createRandomTempFile. Keeps test helpers available via static imports while making the test class plain. --- .../src/test/java/io/microsphere/util/jar/JarUtilsTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java index 4a90ec23e..4964e755a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java @@ -3,7 +3,6 @@ */ package io.microsphere.util.jar; -import io.microsphere.AbstractTestCase; import io.microsphere.filter.JarEntryFilter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -16,6 +15,8 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; +import static io.microsphere.AbstractTestCase.createRandomTempDirectory; +import static io.microsphere.AbstractTestCase.createRandomTempFile; import static io.microsphere.net.URLUtils.ofURL; import static io.microsphere.util.ClassLoaderUtils.ResourceType.PACKAGE; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; @@ -49,7 +50,7 @@ * @see JarUtilsTest * @since 1.0.0 */ -class JarUtilsTest extends AbstractTestCase { +class JarUtilsTest { private File targetDirectory; From abf7b2c221b4ba17a7c86bd3c393e96c287e69df Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:45:37 +0800 Subject: [PATCH 291/438] Remove AbstractTestCase from JmxUtilsTest Delete the unused import of io.microsphere.AbstractTestCase and remove the extends AbstractTestCase clause from JmxUtilsTest. Tests themselves are unchanged aside from a minor whitespace adjustment, decoupling the test class from the shared base test class. --- .../src/test/java/io/microsphere/management/JmxUtilsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java index afc103f14..05f33fdf6 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/JmxUtilsTest.java @@ -16,7 +16,6 @@ */ package io.microsphere.management; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -95,7 +94,7 @@ * @author Mercy * @since 1.0.0 */ -class JmxUtilsTest extends AbstractTestCase { +class JmxUtilsTest { private static MBeanServer mBeanServer; @@ -309,6 +308,7 @@ void testFindMBeanAttributeInfoOnNotFoundMBeanInfo() { MBeanAttributeInfo mBeanAttributeInfo = findMBeanAttributeInfo(mBeanServer, notFoundObjectName, notFoundAttributeName); assertNull(mBeanAttributeInfo); } + @Test void testFindMBeanAttributeInfoOnNotFoundAttribute() { MBeanAttributeInfo mBeanAttributeInfo = findMBeanAttributeInfo(mBeanServer, objectName, notFoundAttributeName); From 9e7868454ae180272aaafa2b665b82139c31d209 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:46:35 +0800 Subject: [PATCH 292/438] Use Loggable in ListUtilsTest and clean imports Replace AbstractTestCase with Loggable for test logging, remove unused logging factory import, and tidy imports. Add static imports for TEST_NULL_* and TEST_NULL_LIST constants, and use the static MutableInteger.of import (of(0)) in tests. Minor formatting update (no newline at EOF change). --- .../io/microsphere/collection/ListUtilsTest.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java index a204626dd..2efe188d9 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java @@ -16,9 +16,8 @@ */ package io.microsphere.collection; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import io.microsphere.lang.MutableInteger; -import io.microsphere.logging.Logger; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -27,6 +26,10 @@ import java.util.List; import java.util.Set; +import static io.microsphere.AbstractTestCase.TEST_NULL_ENUMERATION; +import static io.microsphere.AbstractTestCase.TEST_NULL_ITERABLE; +import static io.microsphere.AbstractTestCase.TEST_NULL_ITERATOR; +import static io.microsphere.AbstractTestCase.TEST_NULL_LIST; import static io.microsphere.collection.ListUtils.first; import static io.microsphere.collection.ListUtils.forEach; import static io.microsphere.collection.ListUtils.isList; @@ -37,7 +40,7 @@ import static io.microsphere.collection.ListUtils.ofArrayList; import static io.microsphere.collection.ListUtils.ofLinkedList; import static io.microsphere.collection.ListUtils.ofList; -import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.lang.MutableInteger.of; import static java.util.Arrays.asList; import static java.util.Collections.emptyEnumeration; import static java.util.Collections.emptyIterator; @@ -58,7 +61,7 @@ * @see ListUtils * @since 1.0.0 */ -class ListUtilsTest extends AbstractTestCase { +class ListUtilsTest extends Loggable { private static final List TEST_LIST = asList("A", "B", "C"); @@ -185,7 +188,7 @@ void testOfLinkedListOnEmptyArray() { @Test void testForEach() { List list = TEST_LIST; - MutableInteger mutableInteger = MutableInteger.of(0); + MutableInteger mutableInteger = of(0); Iterator iterator = list.iterator(); forEach(list, (index, value) -> { log("forEach(index = {} , value = '{}')", index, value); @@ -201,4 +204,4 @@ void testForEach() { assertEquals(iterator2.next(), value); }); } -} +} \ No newline at end of file From 2bd64face0ee0c5407cc828c020a7cfbab2022a0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:47:44 +0800 Subject: [PATCH 293/438] Remove AbstractTestCase base from ListsTest ListsTest no longer extends AbstractTestCase. Instead it statically imports TEST_NULL_OBJECT_ARRAY from io.microsphere.AbstractTestCase and updates imports accordingly. This decouples the test class from the base test class while preserving access to the shared test constant. --- .../src/test/java/io/microsphere/collection/ListsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ListsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ListsTest.java index f6e2c71a2..5cce7323f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ListsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ListsTest.java @@ -1,8 +1,8 @@ package io.microsphere.collection; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; +import static io.microsphere.AbstractTestCase.TEST_NULL_OBJECT_ARRAY; import static io.microsphere.collection.ListUtils.of; import static io.microsphere.collection.Lists.ofList; import static java.util.Collections.emptyList; @@ -15,7 +15,7 @@ * @see Lists * @since 1.0.0 */ -class ListsTest extends AbstractTestCase { +class ListsTest { @Test void testOfList0() { From 2bb33ad5cf122d6b26c0d74bcc6a7cbed54560bb Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:48:28 +0800 Subject: [PATCH 294/438] Remove AbstractTestCase inheritance in test LoggingFileChangedListenerTest no longer extends AbstractTestCase. The test now statically imports createRandomTempFile from AbstractTestCase and the unused class import was removed, decoupling the test from the base test class and using the helper method directly. --- .../microsphere/io/event/LoggingFileChangedListenerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/event/LoggingFileChangedListenerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/event/LoggingFileChangedListenerTest.java index 161c4eb6a..574eee052 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/event/LoggingFileChangedListenerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/event/LoggingFileChangedListenerTest.java @@ -18,13 +18,13 @@ package io.microsphere.io.event; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; +import static io.microsphere.AbstractTestCase.createRandomTempFile; import static io.microsphere.io.event.FileChangedEvent.Kind.CREATED; import static io.microsphere.io.event.FileChangedEvent.Kind.DELETED; import static io.microsphere.io.event.FileChangedEvent.Kind.MODIFIED; @@ -37,7 +37,7 @@ * @see LoggingFileChangedListener * @since 1.0.0 */ -class LoggingFileChangedListenerTest extends AbstractTestCase { +class LoggingFileChangedListenerTest { private LoggingFileChangedListener listener; From 32456cd302dc60653f0668750638a9e3b1f2724e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:48:59 +0800 Subject: [PATCH 295/438] Remove AbstractTestCase inheritance from test Remove the import of io.microsphere.AbstractTestCase and the 'extends AbstractTestCase' clause from ManagementUtilsTest. The test is now a plain JUnit Jupiter test class; test logic and assertions remain unchanged. This removes the unnecessary dependency on the AbstractTestCase base class for this test. --- .../java/io/microsphere/management/ManagementUtilsTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/management/ManagementUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/management/ManagementUtilsTest.java index bb40748ba..f0fe200af 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/management/ManagementUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/management/ManagementUtilsTest.java @@ -1,6 +1,5 @@ package io.microsphere.management; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; import static io.microsphere.management.ManagementUtils.currentProcessId; @@ -15,7 +14,7 @@ * @see ManagementUtils * @since 1.0.0 */ -class ManagementUtilsTest extends AbstractTestCase { +class ManagementUtilsTest { @Test void testGetCurrentProcessId() { @@ -23,5 +22,4 @@ void testGetCurrentProcessId() { assertTrue(processId > 0); assertEquals(currentProcessId, getCurrentProcessId()); } - } \ No newline at end of file From c43bf840e84069309bc24b535712cae907de5840 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:49:30 +0800 Subject: [PATCH 296/438] Use Loggable instead of AbstractTestCase in test Replace AbstractTestCase with Loggable in ProcessExecutorTest: update the import and change the test class to extend Loggable. This simplifies the test base by using the logging helper instead of the previous abstract test class. --- .../test/java/io/microsphere/process/ProcessExecutorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java index 557aa516d..e296b1025 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java @@ -1,6 +1,6 @@ package io.microsphere.process; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import io.microsphere.io.FastByteArrayOutputStream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -20,7 +20,7 @@ * @see ProcessExecutorTest * @since 1.0.0 */ -class ProcessExecutorTest extends AbstractTestCase { +class ProcessExecutorTest extends Loggable { private FastByteArrayOutputStream outputStream; From 41e1e95acd8390cb880e9bcdc900f57d8429036f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:50:04 +0800 Subject: [PATCH 297/438] Use Loggable base class in ProcessManagerTest Replace the test base from io.microsphere.AbstractTestCase to io.microsphere.Loggable in ProcessManagerTest. This updates the import and the class declaration (extends AbstractTestCase -> extends Loggable) to use the Loggable test utility. --- .../test/java/io/microsphere/process/ProcessManagerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java index 445ecffc3..47e666867 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java @@ -1,6 +1,6 @@ package io.microsphere.process; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -23,7 +23,7 @@ * @see ProcessManager * @since 1.0.0 */ -class ProcessManagerTest extends AbstractTestCase { +class ProcessManagerTest extends Loggable { @BeforeEach void setUp() { From 31294fe5bfd68c1c6cad80791c751ae046800109 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:50:44 +0800 Subject: [PATCH 298/438] Remove AbstractTestCase inheritance from test Drop the unused io.microsphere.AbstractTestCase import and remove extends AbstractTestCase from ReflectionUtilsTest. This makes the test class a plain unit test, simplifying setup and removing an unnecessary dependency. --- .../test/java/io/microsphere/reflect/ReflectionUtilsTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java index 6d8c42b7b..3b3d1f8dd 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java @@ -1,6 +1,5 @@ package io.microsphere.reflect; -import io.microsphere.AbstractTestCase; import io.microsphere.test.Data; import org.junit.jupiter.api.Test; @@ -36,7 +35,7 @@ * @see ReflectionUtilsTest * @since 1.0.0 */ -class ReflectionUtilsTest extends AbstractTestCase { +class ReflectionUtilsTest { @Test void testGetCallerClassX() { From 106f4c4ca2c18d8bbed1991a08259e323a838b10 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:51:33 +0800 Subject: [PATCH 299/438] Drop AbstractTestCase from SetUtilsTest Remove dependency on AbstractTestCase by deleting its import and the extends clause. Add a static import for TEST_NULL_STRING_ARRAY and tidy imports/formatting in the test file to simplify the unit test and remove the base test class coupling. --- .../test/java/io/microsphere/collection/SetUtilsTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/SetUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/SetUtilsTest.java index f301ea1f6..2b93805a7 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/SetUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/SetUtilsTest.java @@ -1,6 +1,5 @@ package io.microsphere.collection; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; import java.util.Collection; @@ -9,6 +8,7 @@ import java.util.Map; import java.util.Set; +import static io.microsphere.AbstractTestCase.TEST_NULL_STRING_ARRAY; import static io.microsphere.collection.CollectionUtils.toIterable; import static io.microsphere.collection.EnumerationUtils.ofEnumeration; import static io.microsphere.collection.MapUtils.FIXED_LOAD_FACTOR; @@ -34,7 +34,7 @@ * @see SetUtils * @since 1.0.0 */ -class SetUtilsTest extends AbstractTestCase { +class SetUtilsTest { private static final String[] ELEMENTS = new String[]{"a", "b", "c"}; @@ -165,5 +165,4 @@ private void assertSet(Set set) { assertTrue(set.contains("c")); assertFalse(set.contains("d")); } - } \ No newline at end of file From d569bc7de3bf330a6e5c9a3c1a765b7aa16fe286 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:52:12 +0800 Subject: [PATCH 300/438] Remove AbstractTestCase inheritance from SetsTest Delete the AbstractTestCase import and stop extending it in SetsTest; add a static import for TEST_NULL_OBJECT_ARRAY so the test can reference it directly. This simplifies the test class into a plain JUnit 5 test without changing test behavior. --- .../src/test/java/io/microsphere/collection/SetsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/SetsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/SetsTest.java index e4db76534..afdabee1c 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/SetsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/SetsTest.java @@ -1,8 +1,8 @@ package io.microsphere.collection; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.Test; +import static io.microsphere.AbstractTestCase.TEST_NULL_OBJECT_ARRAY; import static io.microsphere.collection.SetUtils.of; import static io.microsphere.collection.Sets.ofSet; import static java.util.Collections.emptySet; @@ -15,7 +15,7 @@ * @see Sets * @since 1.0.0 */ -class SetsTest extends AbstractTestCase { +class SetsTest { @Test void testOfSet0() { From 04735fb249d2ea96090fcccbac5599004768b1ca Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:53:24 +0800 Subject: [PATCH 301/438] Remove AbstractTestCase inheritance from test Drop the unused AbstractTestCase import and stop extending it in SimpleFileScannerTest. The test no longer depends on the base test class, simplifying the test class declaration. --- .../io/microsphere/io/scanner/SimpleFileScannerTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleFileScannerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleFileScannerTest.java index de9c4844b..e8209df7e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleFileScannerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleFileScannerTest.java @@ -3,7 +3,6 @@ */ package io.microsphere.io.scanner; -import io.microsphere.AbstractTestCase; import io.microsphere.io.filter.DirectoryFileFilter; import io.microsphere.io.filter.NameFileFilter; import org.junit.jupiter.api.Test; @@ -23,7 +22,7 @@ * @see SimpleFileScanner * @since 1.0.0 */ -class SimpleFileScannerTest extends AbstractTestCase { +class SimpleFileScannerTest { private static final SimpleFileScanner simpleFileScanner = INSTANCE; @@ -49,4 +48,4 @@ void testScanOnBinDirectory() { Set directories = simpleFileScanner.scan(JAVA_HOME_DIR, true, new NameFileFilter("bin")); assertEquals(1, directories.size()); } -} +} \ No newline at end of file From 45021e87eecbb21a6cfbeb98969fe75c9baaf24b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:54:11 +0800 Subject: [PATCH 302/438] Extend test with Loggable and add static helpers Replace inheritance from AbstractTestCase with Loggable and add static imports for createRandomFile and createRandomTempDirectory. This updates the test to use the logging base class while pulling test helper methods in as static imports to simplify references in the test file. --- .../io/microsphere/io/StandardFileWatchServiceTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java index f7d1a65a3..52a4473c2 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java @@ -16,7 +16,7 @@ */ package io.microsphere.io; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import io.microsphere.io.event.DefaultFileChangedListener; import io.microsphere.io.event.FileChangedEvent; import io.microsphere.io.event.FileChangedEvent.Kind; @@ -35,6 +35,8 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; +import static io.microsphere.AbstractTestCase.createRandomFile; +import static io.microsphere.AbstractTestCase.createRandomTempDirectory; import static io.microsphere.concurrent.ExecutorUtils.shutdown; import static io.microsphere.io.FileUtils.deleteDirectory; import static io.microsphere.io.FileUtils.forceDelete; @@ -71,7 +73,7 @@ * @author Mercy * @since 1.0.0 */ -class StandardFileWatchServiceTest extends AbstractTestCase { +class StandardFileWatchServiceTest extends Loggable { private File testDir; From e86e2ae9e9ade09857bf51c653f1a84504f8fb0b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:54:41 +0800 Subject: [PATCH 303/438] Use TEST_CLASS_LOADER in file watch test Replace use of super.classLoader with TEST_CLASS_LOADER in StandardFileWatchServiceTest.testFile and add a static import for TEST_CLASS_LOADER. This ensures the shared test class loader is used for resource lookup instead of referencing the superclass instance field. --- .../java/io/microsphere/io/StandardFileWatchServiceTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java index 52a4473c2..020c92afa 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java @@ -35,6 +35,7 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; +import static io.microsphere.AbstractTestCase.TEST_CLASS_LOADER; import static io.microsphere.AbstractTestCase.createRandomFile; import static io.microsphere.AbstractTestCase.createRandomTempDirectory; import static io.microsphere.concurrent.ExecutorUtils.shutdown; @@ -121,7 +122,7 @@ void testToKindOnOverflow() { @Test void testFile() throws Exception { - URL resource = getResource(super.classLoader, "test.txt"); + URL resource = getResource(TEST_CLASS_LOADER, "test.txt"); String resourceFilePath = resource.getFile(); File sourceFile = new File(resourceFilePath); From 267232411ca3727601fd8516952596270d712a93 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:55:25 +0800 Subject: [PATCH 304/438] Use Loggable base class in StopWatchTest Replace AbstractTestCase with Loggable in StopWatchTest and remove explicit Logger and getLogger imports. Clean up unused imports and adjust class inheritance to rely on Loggable for logging support. --- .../src/test/java/io/microsphere/util/StopWatchTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java index 09a4832b3..e811476f0 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java @@ -16,14 +16,12 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; -import io.microsphere.logging.Logger; +import io.microsphere.Loggable; import io.microsphere.util.StopWatch.Task; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static io.microsphere.constants.SymbolConstants.SPACE; -import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.util.StopWatch.Task.start; import static java.lang.Thread.sleep; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -42,7 +40,7 @@ * @author Mercy * @since 1.0.0 */ -class StopWatchTest extends AbstractTestCase { +class StopWatchTest extends Loggable { private static final String testName = "test"; From c53b5ebf94ff9ed7863821bac7fb4651238bc61e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:56:04 +0800 Subject: [PATCH 305/438] Remove AbstractTestCase inheritance in test StringBuilderWriterTest no longer extends AbstractTestCase. Instead, required utilities (TEST_NULL_STRING and random) are imported statically from AbstractTestCase. Cleans up test class to avoid inheritance-based coupling and use static helpers directly. --- .../test/java/io/microsphere/io/StringBuilderWriterTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/StringBuilderWriterTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/StringBuilderWriterTest.java index 984a51d92..f910acef5 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/StringBuilderWriterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/StringBuilderWriterTest.java @@ -17,10 +17,11 @@ package io.microsphere.io; -import io.microsphere.AbstractTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static io.microsphere.AbstractTestCase.TEST_NULL_STRING; +import static io.microsphere.AbstractTestCase.random; import static io.microsphere.util.StringUtils.EMPTY_STRING; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,7 +32,7 @@ * @see StringBuilderWriter * @since 1.0.0 */ -class StringBuilderWriterTest extends AbstractTestCase { +class StringBuilderWriterTest { private StringBuilderWriter writer; From 577d4a97a364cb7dfdb9ae06ee98a75b8b52a707 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:56:37 +0800 Subject: [PATCH 306/438] Use Loggable base class in ThrowableActionTest Replace AbstractTestCase with io.microsphere.Loggable in ThrowableActionTest. The import and class declaration were updated so the test now extends Loggable, enabling use of the logging helper instead of the previous AbstractTestCase. --- .../io/microsphere/lang/function/ThrowableActionTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java index 8b7786c04..297b6ee34 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java @@ -16,7 +16,7 @@ */ package io.microsphere.lang.function; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Test; import static io.microsphere.lang.function.ThrowableAction.execute; @@ -27,7 +27,7 @@ * * @since 1.0.0 */ -class ThrowableActionTest extends AbstractTestCase { +class ThrowableActionTest extends Loggable { private final ThrowableAction action = () -> { log("ThrowableAction#execute()"); From 28413a858271d2611bd1cb5a70d28d96709b499c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:57:04 +0800 Subject: [PATCH 307/438] Use Loggable in ThrowableBiConsumerTest Replace AbstractTestCase with Loggable as the superclass in ThrowableBiConsumerTest and update the import. This switches the test to use logging utilities provided by Loggable instead of the previous test base. --- .../io/microsphere/lang/function/ThrowableBiConsumerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java index b45b6c2ac..65d9107f1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java @@ -17,7 +17,7 @@ package io.microsphere.lang.function; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; import org.junit.jupiter.api.Test; /** @@ -27,7 +27,7 @@ * @see ThrowableBiConsumer * @since 1.0.0 */ -class ThrowableBiConsumerTest extends AbstractTestCase { +class ThrowableBiConsumerTest extends Loggable { @Test void testAccept() throws Throwable { From c4e7894264c13f4bf960e9cd74553e1738c1c029 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:57:21 +0800 Subject: [PATCH 308/438] Remove trailing newline in ThrowableBiConsumerTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove trailing newline at end of microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java. No functional changes — only EOF newline removal. --- .../io/microsphere/lang/function/ThrowableBiConsumerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java index 65d9107f1..58ab7a4cf 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java @@ -46,4 +46,4 @@ void testAndThen() throws Throwable { log("andThen -> The String is : {}, the Integer is : {}", s, i); }).accept("Mercy", 1); } -} +} \ No newline at end of file From 41fcabc6c6e52384969ea04ba973fa38280a02e6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:58:36 +0800 Subject: [PATCH 309/438] Make test helper methods public static Change several test helper methods in AbstractTestCase to be public static so they can be invoked without an instance. Updated modifiers for assertThrowable, both assertValues overloads, and assertType to increase reusability across tests and static utility contexts. --- .../src/test/java/io/microsphere/AbstractTestCase.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java index 393fee5b7..a4ab1c011 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java +++ b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java @@ -175,7 +175,7 @@ public static File makeLinkFile(File targetFile) throws Exception { return linkFile; } - protected void assertThrowable(ThrowableAction action, Consumer failureHandler) { + public static void assertThrowable(ThrowableAction action, Consumer failureHandler) { Throwable failure = null; try { action.execute(); @@ -186,16 +186,16 @@ protected void assertThrowable(ThrowableAction action, Consumer failu failureHandler.accept(failure); } - protected void assertValues(List values, Object... expectedValues) { + public static void assertValues(List values, Object... expectedValues) { assertValues(values, expectedValues.length, expectedValues); } - protected void assertValues(List values, int expectedSize, Object... expectedTypes) { + public static void assertValues(List values, int expectedSize, Object... expectedTypes) { assertEquals(expectedSize, values.size()); assertEquals(newHashSet(expectedTypes), newHashSet(values)); } - protected void assertType(Type expect, Type actual) { + public static void assertType(Type expect, Type actual) { assertEquals(asClass(expect), asClass(actual)); } } From 2268f3073154c47994973602f1c03a731dc22d4b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:58:45 +0800 Subject: [PATCH 310/438] Use static assertValues in TypeFinderTest Remove inheritance from AbstractTestCase and add a static import of assertValues. The test class no longer extends the base test case and instead uses the assertValues utility directly; no other behavioral changes to the tests. --- .../src/test/java/io/microsphere/util/TypeFinderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java index 1c5b3b44e..fa0ed880d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java @@ -1,6 +1,5 @@ package io.microsphere.util; -import io.microsphere.AbstractTestCase; import io.microsphere.test.BF3; import io.microsphere.test.StringBF2; import io.microsphere.test.StringIntegerF1; @@ -15,6 +14,7 @@ import java.util.List; import java.util.function.BiFunction; +import static io.microsphere.AbstractTestCase.assertValues; import static io.microsphere.reflect.TypeUtils.NON_OBJECT_TYPE_FILTER; import static io.microsphere.util.TypeFinder.Include.HIERARCHICAL; import static io.microsphere.util.TypeFinder.Include.INTERFACES; @@ -36,7 +36,7 @@ * @see io.microsphere.util.ClassFinder * @since 1.0.0 */ -class TypeFinderTest extends AbstractTestCase { +class TypeFinderTest { @Test void testConstructorOnNullType() { From 842c344025022ea39d61dc7cb1ed21fd8d4f37ec Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 21:59:24 +0800 Subject: [PATCH 311/438] Remove AbstractTestCase inheritance from TypeUtilsTest Decouple TypeUtilsTest from the AbstractTestCase base by removing the extends clause and the direct import. Add a static import for assertValues to keep assertions working. Also clean up minor import/EOF formatting and stray commented lines. --- .../test/java/io/microsphere/reflect/TypeUtilsTest.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/TypeUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/TypeUtilsTest.java index d65f5850e..13f8a4502 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/TypeUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/TypeUtilsTest.java @@ -16,7 +16,6 @@ */ package io.microsphere.reflect; -import io.microsphere.AbstractTestCase; import io.microsphere.convert.Converter; import io.microsphere.convert.StringToIntegerConverter; import io.microsphere.convert.StringToStringConverter; @@ -43,6 +42,7 @@ import java.util.function.BiFunction; import java.util.function.Predicate; +import static io.microsphere.AbstractTestCase.assertValues; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.reflect.MethodUtils.findMethod; import static io.microsphere.reflect.TypeUtils.GENERIC_ARRAY_TYPE_FILTER; @@ -112,7 +112,7 @@ * @author Mercy * @since 1.0.0 */ -class TypeUtilsTest extends AbstractTestCase { +class TypeUtilsTest { @Test void testConstantFilters() { @@ -1093,7 +1093,4 @@ private void assertEGenericInterfaces(List types) { assertDGenericInterfaces(types); } -} - - -// MyHashMap -> HashMap +} \ No newline at end of file From ef8e519aacedbd13455e24c03b9614032a671621 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 25 Feb 2026 22:00:54 +0800 Subject: [PATCH 312/438] Use TEST_* constants in URLUtilsTest Remove AbstractTestCase inheritance and unused import; add static imports for TEST_CLASS_LOADER and TEST_NULL_STRING. Replace usages of the instance classLoader with TEST_CLASS_LOADER in getClassResource/getResource calls and adjust imports accordingly to simplify test setup while preserving behavior. --- .../java/io/microsphere/net/URLUtilsTest.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java index 1f974e2d8..923ddd112 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java @@ -3,7 +3,6 @@ */ package io.microsphere.net; -import io.microsphere.AbstractTestCase; import io.microsphere.net.console.Handler; import io.microsphere.util.StringUtils; import org.junit.jupiter.api.AfterEach; @@ -21,6 +20,8 @@ import java.util.List; import java.util.Map; +import static io.microsphere.AbstractTestCase.TEST_CLASS_LOADER; +import static io.microsphere.AbstractTestCase.TEST_NULL_STRING; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.newHashMap; import static io.microsphere.collection.MapUtils.ofMap; @@ -100,7 +101,7 @@ * @see URLUtilsTest * @since 1.0.0 */ -class URLUtilsTest extends AbstractTestCase { +class URLUtilsTest { private static final String TEST_PATH = "/abc/def"; @@ -159,7 +160,7 @@ void testOfURLOnFailed() { @Test void testResolveArchiveEntryPath() { - URL resourceURL = getClassResource(classLoader, Nonnull.class); + URL resourceURL = getClassResource(TEST_CLASS_LOADER, Nonnull.class); String expectedPath = "javax/annotation/Nonnull.class"; String relativePath = resolveArchiveEntryPath(resourceURL); assertEquals(expectedPath, relativePath); @@ -324,10 +325,10 @@ void testIsDirectoryURL() { assertFalse(isDirectoryURL(classFileURL)); - URL resourceURL = getClassResource(classLoader, StringUtils.class); + URL resourceURL = getClassResource(TEST_CLASS_LOADER, StringUtils.class); assertFalse(isDirectoryURL(resourceURL)); - resourceURL = getResource(this.classLoader, PACKAGE, "javax.annotation"); + resourceURL = getResource(TEST_CLASS_LOADER, PACKAGE, "javax.annotation"); assertTrue(isDirectoryEntry(resourceURL)); String externalForm = null; @@ -335,10 +336,10 @@ void testIsDirectoryURL() { resourceURL = ofURL(externalForm); assertTrue(isDirectoryURL(resourceURL)); - resourceURL = getClassResource(classLoader, String.class); + resourceURL = getClassResource(TEST_CLASS_LOADER, String.class); assertFalse(isDirectoryURL(resourceURL)); - resourceURL = getClassResource(classLoader, getClass()); + resourceURL = getClassResource(TEST_CLASS_LOADER, getClass()); assertFalse(isDirectoryURL(resourceURL)); externalForm = substringBeforeLast(resourceURL.toExternalForm(), getClass().getSimpleName() + CLASS_EXTENSION); From 40ba3ee0a2a98cc7de61efc6731845f0923d8714 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 10:29:36 +0800 Subject: [PATCH 313/438] Use isNotEmpty for authority check Replace manual null-and-empty check on the authority string with a utility method isNotEmpty and add the corresponding static import. This cleans up the conditional in Handler.java (classpath URL handling) without changing behavior. --- .../src/main/java/io/microsphere/net/classpath/Handler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/classpath/Handler.java b/microsphere-java-core/src/main/java/io/microsphere/net/classpath/Handler.java index 7cfafd331..ce829fbaa 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/classpath/Handler.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/classpath/Handler.java @@ -26,6 +26,7 @@ import java.net.URLStreamHandler; import static io.microsphere.constants.PathConstants.SLASH_CHAR; +import static io.microsphere.util.CharSequenceUtils.isNotEmpty; import static io.microsphere.util.ClassLoaderUtils.getClassLoader; /** @@ -45,7 +46,7 @@ public URLConnection openConnection(URL u, Proxy proxy) throws IOException { String classPath = path; - if (authority != null && !authority.isEmpty()) { + if (isNotEmpty(authority)) { classPath = authority + path; } From 77c43386bc1bad8eb262e127a6ae62c82ba4c5c7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 10:32:52 +0800 Subject: [PATCH 314/438] Fix MemberUtilsTest assertions to use isAbstract Replace incorrect isStatic assertions with isAbstract in MemberUtilsTest (tests testIsAbstractOnNonAbstract and testIsAbstractOnNull) so the tests check for abstractness as intended. --- .../src/test/java/io/microsphere/reflect/MemberUtilsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MemberUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MemberUtilsTest.java index e8d545533..c2af07786 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MemberUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MemberUtilsTest.java @@ -83,12 +83,12 @@ void testIsAbstract() { @Test void testIsAbstractOnNonAbstract() { - assertFalse(isStatic(findMethod(ReflectionTest.class, "privateMethod"))); + assertFalse(isAbstract(findMethod(ReflectionTest.class, "privateMethod"))); } @Test void testIsAbstractOnNull() { - assertFalse(isStatic(null)); + assertFalse(isAbstract(null)); } @Test From 1e6711526be1a51f63e477fdb367661c2774986d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 10:34:50 +0800 Subject: [PATCH 315/438] Introduce CASE_SENSITIVE constant Add a CASE_SENSITIVE constant to FileExtensionFilter to centralize the OS-based case sensitivity logic (true on non-Windows) and replace inline !IS_OS_WINDOWS checks with this constant for clarity and maintainability. No behavioral change intended. --- .../java/io/microsphere/io/filter/FileExtensionFilter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/filter/FileExtensionFilter.java b/microsphere-java-core/src/main/java/io/microsphere/io/filter/FileExtensionFilter.java index 1436c4bbe..6115094f6 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/filter/FileExtensionFilter.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/filter/FileExtensionFilter.java @@ -56,6 +56,8 @@ */ public class FileExtensionFilter implements IOFileFilter { + private static final boolean CASE_SENSITIVE = !IS_OS_WINDOWS; + private final String extension; protected FileExtensionFilter(String extension) { @@ -75,7 +77,7 @@ public boolean accept(File file) { return false; } - return matches(fileExtension, !IS_OS_WINDOWS); + return matches(fileExtension, CASE_SENSITIVE); } protected boolean matches(String fileExtension, boolean caseSensitive) { From ed547b35b2ea2496aa3001546f1e56c6545fe715 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 10:54:54 +0800 Subject: [PATCH 316/438] Update pom.xml --- microsphere-java-parent/pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-parent/pom.xml b/microsphere-java-parent/pom.xml index 46ecbbcb2..2e23ea059 100644 --- a/microsphere-java-parent/pom.xml +++ b/microsphere-java-parent/pom.xml @@ -29,6 +29,7 @@ 1.5.27 6.0.3 + 5.14.2 1.37 @@ -157,4 +158,4 @@ - \ No newline at end of file + From 9f9c8c3e8bd0a5d2c79840f8f74c7d3abc282281 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 11:35:28 +0800 Subject: [PATCH 317/438] Add test: testExecute3OnHandleException Adds a new unit test in ThrowableConsumerTest that calls execute(String, ThrowableConsumer, BiConsumer) with a no-op exception handler to verify the exception-handling path does not propagate an error. Improves coverage for the execute-with-handler scenario. --- .../microsphere/lang/function/ThrowableConsumerTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableConsumerTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableConsumerTest.java index ed91c7f90..dcae2fee8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableConsumerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableConsumerTest.java @@ -61,6 +61,12 @@ void testExecute3() { }); } + @Test + void testExecute3OnHandleException() { + execute("For testing", throwableConsumer, (t, e) -> { + }); + } + @Test void testExecute3OnException() { assertThrows(RuntimeException.class, () -> execute("For testing", m -> { @@ -69,4 +75,4 @@ void testExecute3OnException() { throw new RuntimeException(t, e); })); } -} +} \ No newline at end of file From 1547458f46b347248886bf994cedc58552448206 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 11:54:02 +0800 Subject: [PATCH 318/438] Add abstract parameterized LoggingTest Introduce an abstract test utility LoggingTest that manages Logback logger levels for tests. The class (microsphere-java-core/src/test/java/io/microsphere/LoggingTest.java) uses JUnit 5 annotations to run parameterized (INFO, TRACE) scenarios, with @BeforeEach/@AfterEach methods to set and restore the logger level via a shared LoggerContext. The test is currently @Disabled and provides getTargetLogger() to target the package logger. --- .../test/java/io/microsphere/LoggingTest.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/LoggingTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/LoggingTest.java b/microsphere-java-core/src/test/java/io/microsphere/LoggingTest.java new file mode 100644 index 000000000..578b52537 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/LoggingTest.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +package io.microsphere; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.Parameter; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.ValueSource; + +import static ch.qos.logback.classic.Level.valueOf; +import static org.slf4j.LoggerFactory.getILoggerFactory; + +/** + * The abstract class for loggging with repeated levels + * + * @author Mercy + * @see LoggerContext + * @since 1.0.0 + */ +@ParameterizedClass +@ValueSource(strings = {"INFO", "TRACE"}) +@Disabled +public abstract class LoggingTest { + + private static final LoggerContext loggerContext = (LoggerContext) getILoggerFactory(); + + @Parameter + private String logLevel; + + private Level orginalLevel; + + @BeforeEach + final void setLoggingLevel() { + Logger logger = getTargetLogger(); + this.orginalLevel = logger.getLevel(); + Level level = valueOf(logLevel); + logger.setLevel(level); + } + + @AfterEach + final void resetLoggingLevel() { + Logger logger = getTargetLogger(); + logger.setLevel(this.orginalLevel); + } + + protected Logger getTargetLogger() { + return loggerContext.getLogger(getClass().getPackage().getName()); + } +} From c00043c3df0e0d90e10c02353d36b25a078323ac Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 11:54:15 +0800 Subject: [PATCH 319/438] Make ShutdownHookCallbacksThreadTest extend LoggingTest Import LoggingTest and have ShutdownHookCallbacksThreadTest extend it so the test can leverage shared logging utilities/capture. Also remove an extra trailing blank line. --- .../io/microsphere/util/ShutdownHookCallbacksThreadTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallbacksThreadTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallbacksThreadTest.java index 26dfe3b1e..36a25dd7b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallbacksThreadTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallbacksThreadTest.java @@ -1,5 +1,6 @@ package io.microsphere.util; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.Test; import static io.microsphere.util.ShutdownHookCallbacksThread.INSTANCE; @@ -12,7 +13,7 @@ * @see ShutdownHookCallbacksThread * @since 1.0.0 */ -class ShutdownHookCallbacksThreadTest { +class ShutdownHookCallbacksThreadTest extends LoggingTest { @Test void testRun() { @@ -25,6 +26,5 @@ void testRun() { } thread.run(); - } } \ No newline at end of file From 6614752c9d6791663eded09dd7d6abf507cf299f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 11:56:55 +0800 Subject: [PATCH 320/438] Have test class extend LoggingTest Update ClassPathResourceConfigurationPropertyLoaderTest to extend LoggingTest and add the corresponding import. This enables logging support/capture for the test class (facilitating inspection of logs during test execution). --- .../ClassPathResourceConfigurationPropertyLoaderTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/metadata/ClassPathResourceConfigurationPropertyLoaderTest.java b/microsphere-java-core/src/test/java/io/microsphere/metadata/ClassPathResourceConfigurationPropertyLoaderTest.java index 2850b6724..a1e54172a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/metadata/ClassPathResourceConfigurationPropertyLoaderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/metadata/ClassPathResourceConfigurationPropertyLoaderTest.java @@ -18,6 +18,7 @@ package io.microsphere.metadata; +import io.microsphere.LoggingTest; import io.microsphere.beans.ConfigurationProperty; import org.junit.jupiter.api.Test; @@ -35,7 +36,7 @@ * @see ClassPathResourceConfigurationPropertyLoader * @since 1.0.0 */ -class ClassPathResourceConfigurationPropertyLoaderTest { +class ClassPathResourceConfigurationPropertyLoaderTest extends LoggingTest { @Test void testLoadWithSingleResource() throws Throwable { From 22f05dcc1649e13a62429d167c15a44244a9b57a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 11:58:07 +0800 Subject: [PATCH 321/438] Extend ArtifactDetectorTest with LoggingTest Make ArtifactDetectorTest extend LoggingTest to enable logging support in the test. Added import for io.microsphere.LoggingTest and updated the class declaration so tests can leverage the shared logging test utilities. --- .../java/io/microsphere/classloading/ArtifactDetectorTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactDetectorTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactDetectorTest.java index 47161aa8c..efb90f837 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactDetectorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactDetectorTest.java @@ -17,6 +17,7 @@ package io.microsphere.classloading; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.Test; import java.util.List; @@ -33,7 +34,7 @@ * @see ArtifactDetector * @since 1.0.0 */ -class ArtifactDetectorTest { +class ArtifactDetectorTest extends LoggingTest { @Test void testDetect() { From 226fd8bd5046af26eb698cce47cae83dd74fb498 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:08:08 +0800 Subject: [PATCH 322/438] Guard trace logging with isTraceEnabled check Wrap the logger.trace call in JarUtils with a logger.isTraceEnabled() check to avoid unnecessary message formatting and exception handling overhead when trace logging is disabled. This change improves performance on the error path when opening a JarFile fails. --- .../src/main/java/io/microsphere/util/jar/JarUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java index 0fd1a9d73..de940e334 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/jar/JarUtils.java @@ -89,7 +89,9 @@ public static JarFile toJarFile(URL jarURL) throws IllegalArgumentException { try { jarFile = new JarFile(jarAbsolutePath); } catch (IOException e) { - logger.trace("The JarFile can't be open from the url : {}", jarURL, e); + if (logger.isTraceEnabled()) { + logger.trace("The JarFile can't be open from the url : {}", jarURL, e); + } } return jarFile; } From f7d89a66179c4f85c3874a6056089c51556e544c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:08:14 +0800 Subject: [PATCH 323/438] Make JarUtilsTest extend LoggingTest Import LoggingTest and have JarUtilsTest extend it so test logging utilities are available. This enables shared logging setup/behavior for the JarUtils tests to improve diagnostics during test runs. --- .../src/test/java/io/microsphere/util/jar/JarUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java index 4964e755a..11c03dea8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/jar/JarUtilsTest.java @@ -3,6 +3,7 @@ */ package io.microsphere.util.jar; +import io.microsphere.LoggingTest; import io.microsphere.filter.JarEntryFilter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -50,7 +51,7 @@ * @see JarUtilsTest * @since 1.0.0 */ -class JarUtilsTest { +class JarUtilsTest extends LoggingTest { private File targetDirectory; From 7ced9ea56e286b1b4c85fde2cd39defba1e772fd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:09:01 +0800 Subject: [PATCH 324/438] Guard trace logs with isTraceEnabled checks Wrap two logger.trace calls in ClassUtils.java with logger.isTraceEnabled() checks (in class path scanning and in the SimpleJarEntryScanner exception handler). This avoids unnecessary logging overhead/formatting when TRACE is disabled and preserves existing behavior when enabled. --- .../src/main/java/io/microsphere/util/ClassUtils.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java index 53a1430bf..c9166b8fa 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassUtils.java @@ -1119,7 +1119,9 @@ public static Set findClassNamesInClassPath(@Nullable File classPath, bo classNames = findClassNamesInJarFile(classPath, recursive); } - logger.trace("To find the class names in the class path['{}' , recursive : {}] : {}", classPath, recursive, classNames); + if (logger.isTraceEnabled()) { + logger.trace("To find the class names in the class path['{}' , recursive : {}] : {}", classPath, recursive, classNames); + } return classNames; } @@ -1218,8 +1220,10 @@ public static Set findClassNamesInJarFile(@Nullable File jarFile, boolea } } catch (Exception e) { classNames = emptySet(); - logger.trace("The class names can't be resolved by SimpleJarEntryScanner#scan(jarFile = {} ," + - " recursive = {} , jarEntryFilter = ClassFileJarEntryFilter)", jarFile, recursive, e); + if (logger.isTraceEnabled()) { + logger.trace("The class names can't be resolved by SimpleJarEntryScanner#scan(jarFile = {} ," + + " recursive = {} , jarEntryFilter = ClassFileJarEntryFilter)", jarFile, recursive, e); + } } return classNames; } From ed84b2688492a7d2018f4a4d864fa7406756b1d8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:09:06 +0800 Subject: [PATCH 325/438] Make ClassUtilsTest extend LoggingTest Add import for io.microsphere.LoggingTest and change ClassUtilsTest to extend LoggingTest so the test can leverage shared logging/test utilities. No other test logic was modified. --- .../src/test/java/io/microsphere/util/ClassUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java index 185715140..0c7623b60 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassUtilsTest.java @@ -3,6 +3,7 @@ */ package io.microsphere.util; +import io.microsphere.LoggingTest; import io.microsphere.lang.ClassDataRepository; import io.microsphere.test.A; import org.junit.jupiter.api.Test; @@ -110,7 +111,7 @@ * @see ClassUtilsTest * @since 1.0.0 */ -class ClassUtilsTest { +class ClassUtilsTest extends LoggingTest { @Test void testConstants() { From 78dac16430af72cf46cd10c67e835d26745e446d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:09:50 +0800 Subject: [PATCH 326/438] Guard logger.trace with isTraceEnabled() Wrap trace-level logging calls in FieldUtils with logger.isTraceEnabled() checks to avoid unnecessary string construction when trace logging is disabled. Changes apply to findField and the exception handlers handleIllegalAccessException and handleIllegalArgumentException. No functional change to exception behavior; this is an optimization to reduce logging overhead. --- .../main/java/io/microsphere/reflect/FieldUtils.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java index fe139f1d3..3e5bcafce 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java @@ -115,7 +115,9 @@ public static Field findField(Class klass, String fieldName) { // ignore, try the super class field = findField(klass.getSuperclass(), fieldName); } - logger.trace("To find the field[name :'{}'] from the class : '{}'", fieldName, klass); + if (logger.isTraceEnabled()) { + logger.trace("To find the field[name :'{}'] from the class : '{}'", fieldName, klass); + } return field; } @@ -682,14 +684,18 @@ public static void assertFieldMatchType(Object instance, String fieldName, Class static void handleIllegalAccessException(IllegalAccessException e, Object instance, Field field, boolean accessible) { String errorMessage = format("The instance [object : {} , class : {} ] can't access the field[name : '{}' , type : {} , accessible : {}]", instance, getTypeName(instance.getClass()), field.getName(), getTypeName(field.getType()), accessible); - logger.trace(errorMessage); + if (logger.isTraceEnabled()) { + logger.trace(errorMessage); + } throw new IllegalStateException(errorMessage, e); } static void handleIllegalArgumentException(IllegalArgumentException e, Object instance, Field field) { String errorMessage = format("The instance[object : {} , class : {}] can't match the field[name : '{}' , type : {}]", instance, getTypeName(instance.getClass()), field.getName(), getTypeName(field.getType())); - logger.trace(errorMessage); + if (logger.isTraceEnabled()) { + logger.trace(errorMessage); + } throw new IllegalArgumentException(errorMessage, e); } From 1049f2e50c7947f7af57712472a0106d01c3799a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:09:58 +0800 Subject: [PATCH 327/438] Make FieldUtilsTest extend LoggingTest Add io.microsphere.LoggingTest import and update FieldUtilsTest to extend LoggingTest so the test class inherits logging/diagnostic behavior. No other logic changes were made. --- .../src/test/java/io/microsphere/reflect/FieldUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java index 27be787b9..3b507864a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java @@ -16,6 +16,7 @@ */ package io.microsphere.reflect; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -48,7 +49,7 @@ * @author Mercy * @since 1.0.0 */ -class FieldUtilsTest { +class FieldUtilsTest extends LoggingTest { private static String value = "1"; From 635ed52db3ea5f3937ffd7d7e01cf0bae5fe758d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:11:09 +0800 Subject: [PATCH 328/438] Guard trace logging with isTraceEnabled Add a logger.isTraceEnabled() check before invoking logger.trace in ManifestArtifactResourceResolver. This avoids unnecessary string formatting and potential expensive artifact representation creation when trace logging is disabled, reducing runtime overhead without changing behavior. --- .../classloading/ManifestArtifactResourceResolver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/ManifestArtifactResourceResolver.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/ManifestArtifactResourceResolver.java index 97f79ff08..3521245dc 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/ManifestArtifactResourceResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/ManifestArtifactResourceResolver.java @@ -207,7 +207,9 @@ Artifact resolveArtifactMetaInfoInManifest(Manifest manifest, URL resourceURL) { } String version = resolveVersion(mainAttributes); Artifact artifact = create(artifactId, version, resourceURL); - logger.trace("The artifactId was resolved from the resource URL['{}']: {}", artifactId, artifact); + if (logger.isTraceEnabled()) { + logger.trace("The artifactId was resolved from the resource URL['{}']: {}", artifactId, artifact); + } return artifact; } From 420dc40e152e1ffcd0341eb2d6a0892d894c2782 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:11:16 +0800 Subject: [PATCH 329/438] Guard trace logs with isTraceEnabled() Wrap existing logger.trace(...) calls with logger.isTraceEnabled() checks to avoid unnecessary string formatting when trace logging is disabled. Applied to three locations in StreamArtifactResourceResolver where missing archive, missing JarEntry, or missing artifact metadata file are logged. No behavioral changes beyond logging performance improvement. --- .../classloading/StreamArtifactResourceResolver.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/StreamArtifactResourceResolver.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/StreamArtifactResourceResolver.java index 58ed3de1a..0b4312624 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/StreamArtifactResourceResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/StreamArtifactResourceResolver.java @@ -113,7 +113,9 @@ public final Artifact resolve(URL resourceURL) { Artifact artifact = null; try { if (archiveFile == null) { - logger.trace("The resourceURL['{}'] can't be resolved to be an archive file", resourceURL); + if (logger.isTraceEnabled()) { + logger.trace("The resourceURL['{}'] can't be resolved to be an archive file", resourceURL); + } artifactMetadataData = readArtifactMetadataDataFromResource(resourceURL, classLoader); } else { artifactMetadataData = readArtifactMetadataDataFromArchiveFile(archiveFile); @@ -149,7 +151,9 @@ protected InputStream readArtifactMetadataDataFromFile(File archiveFile) throws JarFile jarFile = new JarFile(archiveFile); JarEntry jarEntry = findArtifactMetadataEntry(jarFile); if (jarEntry == null) { - logger.trace("The artifact metadata entry can't be resolved from the JarFile[path: '{}']", archiveFile); + if (logger.isTraceEnabled()) { + logger.trace("The artifact metadata entry can't be resolved from the JarFile[path: '{}']", archiveFile); + } return null; } return jarFile.getInputStream(jarEntry); @@ -159,7 +163,9 @@ protected InputStream readArtifactMetadataDataFromFile(File archiveFile) throws protected InputStream readArtifactMetadataDataFromDirectory(File directory) throws IOException { File artifactMetadataFile = findArtifactMetadata(directory); if (artifactMetadataFile == null) { - logger.trace("The artifact metadata file can't be found in the directory[path: '{}']", directory); + if (logger.isTraceEnabled()) { + logger.trace("The artifact metadata file can't be found in the directory[path: '{}']", directory); + } return null; } return new FileInputStream(artifactMetadataFile); From ac64d1d75e3e6a501c8fd7b7d841560276fd5dd1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:11:35 +0800 Subject: [PATCH 330/438] Extend resolver test with LoggingTest Import LoggingTest and make AbstractArtifactResourceResolverTest extend it so the artifact resolver tests can use shared logging utilities and capture test logs consistently. --- .../classloading/AbstractArtifactResourceResolverTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/AbstractArtifactResourceResolverTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/AbstractArtifactResourceResolverTest.java index 581f5b1ec..319600b0b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/AbstractArtifactResourceResolverTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/AbstractArtifactResourceResolverTest.java @@ -16,6 +16,7 @@ */ package io.microsphere.classloading; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -35,7 +36,7 @@ * @see AbstractArtifactResourceResolver * @since 1.0.0 */ -abstract class AbstractArtifactResourceResolverTest { +abstract class AbstractArtifactResourceResolverTest extends LoggingTest { static final Class TEST_ANNOTATION_CLASS = Nonnull.class; From 8482399ed7b4a9fb12727d9d9c90867403066564 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:25:04 +0800 Subject: [PATCH 331/438] Convert Loggable to interface; update tests Replace Loggable abstract class with a Loggable interface that provides default logging methods (logger is now obtained per-call). Update test classes to implement Loggable instead of extending it, adjust class/interface declaration orders where necessary, and add a LoggingTest base class to ExecutorUtilsTest (with import). Also update ModifierTest to avoid referencing the removed protected logger field by checking Proxy.h instead. --- .../test/java/io/microsphere/AbstractTestCase.java | 2 +- .../src/test/java/io/microsphere/Loggable.java | 13 +++++++------ .../microsphere/collection/CollectionUtilsTest.java | 2 +- .../io/microsphere/collection/ListUtilsTest.java | 2 +- .../collection/ReadOnlyIteratorTest.java | 2 +- .../DelegatingScheduledExecutorServiceTest.java | 2 +- .../microsphere/concurrent/ExecutorUtilsTest.java | 3 ++- .../io/microsphere/convert/BaseConverterTest.java | 2 +- .../io/microsphere/event/AbstractEventListener.java | 2 +- .../test/java/io/microsphere/io/FileUtilsTest.java | 2 +- .../io/microsphere/io/FileWatchServiceTest.java | 2 +- .../io/StandardFileWatchServiceTest.java | 2 +- .../io/event/FileChangedListenerTest.java | 2 +- .../lang/function/ThrowableActionTest.java | 2 +- .../lang/function/ThrowableBiConsumerTest.java | 2 +- .../performance/AbstractPerformanceTest.java | 2 +- .../io/microsphere/process/ProcessExecutorTest.java | 2 +- .../io/microsphere/process/ProcessManagerTest.java | 2 +- .../reflect/AbstractReflectiveDefinitionTest.java | 2 +- .../java/io/microsphere/reflect/ModifierTest.java | 4 ++-- .../java/io/microsphere/util/ArrayUtilsTest.java | 2 +- .../io/microsphere/util/ClassPathUtilsTest.java | 2 +- .../io/microsphere/util/ShutdownHookCallback.java | 2 +- .../io/microsphere/util/ShutdownHookUtilsTest.java | 2 +- .../java/io/microsphere/util/StopWatchTest.java | 2 +- 25 files changed, 33 insertions(+), 31 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java index a4ab1c011..f8ea785fa 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java +++ b/microsphere-java-core/src/test/java/io/microsphere/AbstractTestCase.java @@ -50,7 +50,7 @@ * @since 1.0.0 */ @Disabled -public abstract class AbstractTestCase extends Loggable { +public abstract class AbstractTestCase implements Loggable { public static final String TEST_ELEMENT = "test"; diff --git a/microsphere-java-core/src/test/java/io/microsphere/Loggable.java b/microsphere-java-core/src/test/java/io/microsphere/Loggable.java index 13aa8c44b..03d5641ac 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/Loggable.java +++ b/microsphere-java-core/src/test/java/io/microsphere/Loggable.java @@ -29,23 +29,24 @@ * @see Logger * @since 1.0.0 */ -public abstract class Loggable { +public interface Loggable { - protected final Logger logger = getLogger(getClass()); - - protected void log(Object object) { + default void log(Object object) { + Logger logger = getLogger(getClass()); if (logger.isTraceEnabled()) { logger.trace(valueOf(object)); } } - protected void log(String object, Object... args) { + default void log(String object, Object... args) { + Logger logger = getLogger(getClass()); if (logger.isTraceEnabled()) { logger.trace(object, args); } } - protected void log(String message, Throwable t) { + default void log(String message, Throwable t) { + Logger logger = getLogger(getClass()); if (logger.isTraceEnabled()) { logger.trace(message, t); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/CollectionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/CollectionUtilsTest.java index 7a2aef4f5..cfe3a7a38 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/CollectionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/CollectionUtilsTest.java @@ -74,7 +74,7 @@ * @author Mercy * @since 1.0.0 */ -class CollectionUtilsTest extends Loggable { +class CollectionUtilsTest implements Loggable { @Test void testIsEmpty() { diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java index 2efe188d9..adb75ead1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java @@ -61,7 +61,7 @@ * @see ListUtils * @since 1.0.0 */ -class ListUtilsTest extends Loggable { +class ListUtilsTest implements Loggable { private static final List TEST_LIST = asList("A", "B", "C"); diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ReadOnlyIteratorTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ReadOnlyIteratorTest.java index 260a1c33a..0867b97a8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ReadOnlyIteratorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ReadOnlyIteratorTest.java @@ -34,7 +34,7 @@ * @see ReadOnlyIterator * @since 1.0.0 */ -public abstract class ReadOnlyIteratorTest extends Loggable { +public abstract class ReadOnlyIteratorTest implements Loggable { Iterator instance = createIterator(); diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java index 87d369b78..212252e6b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingScheduledExecutorServiceTest.java @@ -23,7 +23,7 @@ * @see DelegatingScheduledExecutorService * @since 1.0.0 */ -class DelegatingScheduledExecutorServiceTest extends Loggable { +class DelegatingScheduledExecutorServiceTest implements Loggable { private ScheduledExecutorService delegate = newSingleThreadScheduledExecutor(); diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java index 4babd9e25..106cbeee7 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/ExecutorUtilsTest.java @@ -1,6 +1,7 @@ package io.microsphere.concurrent; import io.microsphere.Loggable; +import io.microsphere.LoggingTest; import io.microsphere.util.ShutdownHookUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -28,7 +29,7 @@ * @see ExecutorUtils * @since 1.0.0 */ -class ExecutorUtilsTest extends Loggable { +class ExecutorUtilsTest extends LoggingTest implements Loggable { private ExecutorService executorService; diff --git a/microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java b/microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java index 08b5e08d9..481858868 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/convert/BaseConverterTest.java @@ -36,7 +36,7 @@ * @see AbstractConverter * @since 1.0.0 */ -abstract class BaseConverterTest extends Loggable { +abstract class BaseConverterTest implements Loggable { protected AbstractConverter converter; diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java b/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java index 68de276a3..36e9c4074 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventListener.java @@ -20,7 +20,7 @@ import java.util.concurrent.atomic.AtomicInteger; -public abstract class AbstractEventListener extends Loggable implements EventListener { +public abstract class AbstractEventListener implements Loggable, EventListener { private final AtomicInteger eventOccurs = new AtomicInteger(0); diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java index 0649d43bb..39c774ee4 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileUtilsTest.java @@ -60,7 +60,7 @@ * @see FileUtils * @since 1.0.0 */ -class FileUtilsTest extends Loggable { +class FileUtilsTest implements Loggable { private final URL classFileResource = getClassResource(TEST_CLASS_LOADER, FileUtilsTest.class); diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java index ca8ece861..6d392e557 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FileWatchServiceTest.java @@ -17,7 +17,7 @@ * @see FileWatchService * @since 1.0.0 */ -class FileWatchServiceTest extends Loggable { +class FileWatchServiceTest implements Loggable { private FileWatchService service = (file, listener, kinds) -> { log("Watching : {} , listener : {} , kinds : {}", file, listener, kinds); diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java index 020c92afa..5a732f1a5 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java @@ -74,7 +74,7 @@ * @author Mercy * @since 1.0.0 */ -class StandardFileWatchServiceTest extends Loggable { +class StandardFileWatchServiceTest implements Loggable { private File testDir; diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/event/FileChangedListenerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/event/FileChangedListenerTest.java index 7bfbfd32a..c65fa3add 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/event/FileChangedListenerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/event/FileChangedListenerTest.java @@ -28,7 +28,7 @@ * @see FileChangedListener * @since */ -class FileChangedListenerTest extends Loggable { +class FileChangedListenerTest implements Loggable { private FileChangedListener listener; diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java index 297b6ee34..5dbe3452f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableActionTest.java @@ -27,7 +27,7 @@ * * @since 1.0.0 */ -class ThrowableActionTest extends Loggable { +class ThrowableActionTest implements Loggable { private final ThrowableAction action = () -> { log("ThrowableAction#execute()"); diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java index 58ab7a4cf..13463dca8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableBiConsumerTest.java @@ -27,7 +27,7 @@ * @see ThrowableBiConsumer * @since 1.0.0 */ -class ThrowableBiConsumerTest extends Loggable { +class ThrowableBiConsumerTest implements Loggable { @Test void testAccept() throws Throwable { diff --git a/microsphere-java-core/src/test/java/io/microsphere/performance/AbstractPerformanceTest.java b/microsphere-java-core/src/test/java/io/microsphere/performance/AbstractPerformanceTest.java index 8a6dfdb49..0ff95947b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/performance/AbstractPerformanceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/performance/AbstractPerformanceTest.java @@ -11,7 +11,7 @@ * @since 1.0.0 */ @Disabled -public abstract class AbstractPerformanceTest extends Loggable { +public abstract class AbstractPerformanceTest implements Loggable { protected void execute(PerformanceAction action) { long startTime = System.nanoTime(); diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java index e296b1025..0c81927b7 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java @@ -20,7 +20,7 @@ * @see ProcessExecutorTest * @since 1.0.0 */ -class ProcessExecutorTest extends Loggable { +class ProcessExecutorTest implements Loggable { private FastByteArrayOutputStream outputStream; diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java index 47e666867..7ad4cfbb1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessManagerTest.java @@ -23,7 +23,7 @@ * @see ProcessManager * @since 1.0.0 */ -class ProcessManagerTest extends Loggable { +class ProcessManagerTest implements Loggable { @BeforeEach void setUp() { diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java index 56c1d0f64..f1c3724fc 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java @@ -30,7 +30,7 @@ * @see ReflectiveDefinition * @since 1.0.0 */ -public abstract class AbstractReflectiveDefinitionTest extends Loggable { +public abstract class AbstractReflectiveDefinitionTest implements Loggable { private final List[] headConstructorArgumentsArray = ofArray( ofList(SINCE, getClassName()), diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java index ecaa38576..c7bee110d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ModifierTest.java @@ -16,9 +16,9 @@ */ package io.microsphere.reflect; -import io.microsphere.Loggable; import org.junit.jupiter.api.Test; +import java.lang.reflect.Proxy; import java.util.AbstractList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.AbstractQueuedSynchronizer; @@ -99,7 +99,7 @@ void testIsPrivate() throws NoSuchFieldException { @Test void testIsProtected() throws NoSuchFieldException { - assertTrue(isProtected(Loggable.class.getDeclaredField("logger").getModifiers())); + assertTrue(isProtected(Proxy.class.getDeclaredField("h").getModifiers())); assertFalse(isProtected(String.class.getModifiers())); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java index 580aca9ed..202159e11 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ArrayUtilsTest.java @@ -89,7 +89,7 @@ * @author Mercy * @since 1.0.0 */ -class ArrayUtilsTest extends Loggable { +class ArrayUtilsTest implements Loggable { @Test void testConstants() { diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java index 66e449a72..bcbc1b748 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java @@ -30,7 +30,7 @@ * @see ClassPathUtilsTest * @since 1.0.0 */ -class ClassPathUtilsTest extends Loggable { +class ClassPathUtilsTest implements Loggable { @Test void testGetBootstrapClassPaths() { diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallback.java b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallback.java index f8305e561..9087a3cab 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallback.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookCallback.java @@ -27,7 +27,7 @@ * @see ShutdownHookUtils * @since 1.0.0 */ -class ShutdownHookCallback extends Loggable implements Runnable, Prioritized { +class ShutdownHookCallback implements Loggable, Runnable, Prioritized { private final int priority; diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java index 89799e3cb..0009475d6 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java @@ -42,7 +42,7 @@ * @author Mercy * @since 1.0.0 */ -class ShutdownHookUtilsTest extends Loggable { +class ShutdownHookUtilsTest implements Loggable { @BeforeEach void setUp() { diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java index e811476f0..55bdb6782 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java @@ -40,7 +40,7 @@ * @author Mercy * @since 1.0.0 */ -class StopWatchTest extends Loggable { +class StopWatchTest implements Loggable { private static final String testName = "test"; From 26b214ca00d042a28b22ce1f40f59248bfa49f90 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:25:18 +0800 Subject: [PATCH 332/438] Guard trace logging with isTraceEnabled Wrap the trace log call in ExecutorUtils.shutdown(...) with a logger.isTraceEnabled() check to avoid unnecessary logging overhead when trace level is disabled. This prevents potential cost from message formatting or parameter evaluation in high-frequency shutdown paths. --- .../main/java/io/microsphere/concurrent/ExecutorUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java b/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java index 930ce67d8..b49c49e93 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/concurrent/ExecutorUtils.java @@ -135,7 +135,9 @@ public static boolean shutdown(ExecutorService executorService) { if (!executorService.isShutdown()) { executorService.shutdown(); } - logger.trace("The ExecutorService({}) has been shutdown", executorService); + if (logger.isTraceEnabled()) { + logger.trace("The ExecutorService({}) has been shutdown", executorService); + } return true; } From 61cfb742434d1c10e789ab4f0aa2b84ec457d357 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:26:02 +0800 Subject: [PATCH 333/438] Guard trace logging with isTraceEnabled Wrap the trace log call in IOUtils with logger.isTraceEnabled() to avoid unnecessary message formatting and argument evaluation when trace level is disabled. This reduces overhead in the stream copy path while preserving the original logging behavior. --- .../src/main/java/io/microsphere/io/IOUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java b/microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java index c5bd0b47e..424454bff 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/IOUtils.java @@ -367,7 +367,9 @@ public static int copy(InputStream in, OutputStream out) throws IOException { byteCount += bytesRead; } out.flush(); - logger.trace("Copied {} bytes[buffer size : {}] from InputStream[{}] to OutputStream[{}]", byteCount, BUFFER_SIZE, in, out); + if (logger.isTraceEnabled()) { + logger.trace("Copied {} bytes[buffer size : {}] from InputStream[{}] to OutputStream[{}]", byteCount, BUFFER_SIZE, in, out); + } return byteCount; } From 26aa1dace5fd6180373f38991e3e77de1cd1c253 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:26:10 +0800 Subject: [PATCH 334/438] Make IOUtilsTest extend LoggingTest Import io.microsphere.LoggingTest and update IOUtilsTest to extend LoggingTest so the test class can reuse shared logging/test utilities (captures logging or common setup/teardown). --- .../src/test/java/io/microsphere/io/IOUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/IOUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/IOUtilsTest.java index 0f5994c10..63ade98f1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/IOUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/IOUtilsTest.java @@ -1,5 +1,6 @@ package io.microsphere.io; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,7 +32,7 @@ * @see IOUtils * @since 1.0.0 */ -class IOUtilsTest { +class IOUtilsTest extends LoggingTest { private static final String TEST_VALUE = "Hello"; From e2bd8ae58e6809b20b4e0bcdd1d35525f9d881d9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:27:15 +0800 Subject: [PATCH 335/438] Guard trace logs with isTraceEnabled Wrap trace logging calls with logger.isTraceEnabled() checks to avoid unnecessary message formatting and potential expensive operations when trace level is disabled. Applied to the file-dispatch log in the event dispatch path and the directory registration log in registerDirectoriesToWatchService. --- .../io/microsphere/io/StandardFileWatchService.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/StandardFileWatchService.java b/microsphere-java-core/src/main/java/io/microsphere/io/StandardFileWatchService.java index ba892d397..80b334813 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/StandardFileWatchService.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/StandardFileWatchService.java @@ -245,7 +245,9 @@ private void dispatchFileChangedEvent(Path filePath, WatchEvent.Kind watchEventK Kind kind = toKind(watchEventKind); FileChangedEvent fileChangedEvent = new FileChangedEvent(file, kind); eventDispatcher.dispatch(fileChangedEvent); - logger.trace("The {} was dispatched", fileChangedEvent); + if (logger.isTraceEnabled()) { + logger.trace("The {} was dispatched", fileChangedEvent); + } } private void registerDirectoriesToWatchService(WatchService watchService) throws Exception { @@ -254,8 +256,10 @@ private void registerDirectoriesToWatchService(WatchService watchService) throws FileChangedMetadata metadata = entry.getValue(); WatchEvent.Kind[] kinds = metadata.watchEventKinds; directoryPath.register(watchService, kinds); - logger.trace("The directory[path : '{}' , event kinds : {}] registers the WatchService : {}", - directoryPath, arrayToString(kinds), watchService); + if (logger.isTraceEnabled()) { + logger.trace("The directory[path : '{}' , event kinds : {}] registers the WatchService : {}", + directoryPath, arrayToString(kinds), watchService); + } } } From c7d216397ee774d1fac7ac5cc8da08d689b1fe74 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:27:29 +0800 Subject: [PATCH 336/438] Make StandardFileWatchServiceTest extend LoggingTest Import io.microsphere.LoggingTest and update StandardFileWatchServiceTest to extend LoggingTest (while retaining Loggable). This enables reuse of logging test utilities in the test class. --- .../java/io/microsphere/io/StandardFileWatchServiceTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java index 5a732f1a5..721d0c541 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/StandardFileWatchServiceTest.java @@ -17,6 +17,7 @@ package io.microsphere.io; import io.microsphere.Loggable; +import io.microsphere.LoggingTest; import io.microsphere.io.event.DefaultFileChangedListener; import io.microsphere.io.event.FileChangedEvent; import io.microsphere.io.event.FileChangedEvent.Kind; @@ -74,7 +75,7 @@ * @author Mercy * @since 1.0.0 */ -class StandardFileWatchServiceTest implements Loggable { +class StandardFileWatchServiceTest extends LoggingTest implements Loggable { private File testDir; From 1f2f4be372adbbdd8c7b1fd3866f70c8b60cd3a9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:27:56 +0800 Subject: [PATCH 337/438] Guard trace logging with isTraceEnabled Wrap the logger.trace call in StandardURLStreamHandlerFactory#createURLStreamHandlerFromDefaultFactory with a logger.isTraceEnabled() check to avoid unnecessary logging overhead and follow conditional logging best practices. --- .../io/microsphere/net/StandardURLStreamHandlerFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java b/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java index 9a23d0c79..71c6161b7 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/StandardURLStreamHandlerFactory.java @@ -86,7 +86,9 @@ URLStreamHandler createURLStreamHandler(Field defaultFactoryField, String protoc URLStreamHandler createURLStreamHandlerFromDefaultFactory(Field defaultFactoryField, String protocol) { if (defaultFactoryField == null) { - logger.trace("The 'defaultFactory' field can't be found in the class URL."); + if (logger.isTraceEnabled()) { + logger.trace("The 'defaultFactory' field can't be found in the class URL."); + } return null; } URLStreamHandlerFactory factory = getStaticFieldValue(defaultFactoryField); From aa2e719fe4c403a863571dc50118ba8c6d6185d3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:28:13 +0800 Subject: [PATCH 338/438] Make test extend LoggingTest Import LoggingTest and have StandardURLStreamHandlerFactoryTest extend it so the test inherits shared logging setup/fixtures. This enables consistent logging behavior for the test class. --- .../microsphere/net/StandardURLStreamHandlerFactoryTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/StandardURLStreamHandlerFactoryTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/StandardURLStreamHandlerFactoryTest.java index 527850016..a75c25ff3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/StandardURLStreamHandlerFactoryTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/StandardURLStreamHandlerFactoryTest.java @@ -18,6 +18,7 @@ package io.microsphere.net; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,7 +31,7 @@ * @see StandardURLStreamHandlerFactory * @since 1.0.0 */ -class StandardURLStreamHandlerFactoryTest { +class StandardURLStreamHandlerFactoryTest extends LoggingTest { private StandardURLStreamHandlerFactory factory; From 5f51bf85ccd5e1f9452edc9f3edc6f4256fb716d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:28:48 +0800 Subject: [PATCH 339/438] Guard trace log with isTraceEnabled Wrap the logger.trace call in URLUtils.java with an if (logger.isTraceEnabled()) check to avoid unnecessary logging message formatting when trace is disabled. Preserves existing behavior (returns null for whitespace in protocol) while reducing runtime overhead during protocol validation. --- .../src/main/java/io/microsphere/net/URLUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java index 8f23b03f0..e8387f49d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/URLUtils.java @@ -1184,7 +1184,9 @@ public static String resolveProtocol(String url) { } for (int i = 0; i <= indexOfColon; i++) { if (isWhitespace(url.charAt(i))) { - logger.trace("The protocol content should not contain the whitespace[url : '{}' , index : {}]", url, i); + if (logger.isTraceEnabled()) { + logger.trace("The protocol content should not contain the whitespace[url : '{}' , index : {}]", url, i); + } return null; } } From 5b8a0d6e01b5bc94eaba4b34b9e34cee6b0c4dbd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:29:03 +0800 Subject: [PATCH 340/438] Make URLUtilsTest extend LoggingTest Add io.microsphere.LoggingTest import and have URLUtilsTest extend LoggingTest so the test can use shared logging/test utilities (enabling log capture/configuration during test execution). --- .../src/test/java/io/microsphere/net/URLUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java index 923ddd112..7b5738ac4 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/URLUtilsTest.java @@ -3,6 +3,7 @@ */ package io.microsphere.net; +import io.microsphere.LoggingTest; import io.microsphere.net.console.Handler; import io.microsphere.util.StringUtils; import org.junit.jupiter.api.AfterEach; @@ -101,7 +102,7 @@ * @see URLUtilsTest * @since 1.0.0 */ -class URLUtilsTest { +class URLUtilsTest extends LoggingTest { private static final String TEST_PATH = "/abc/def"; From 1089014a55c7c01157cdaa09884b034edb12238d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:29:33 +0800 Subject: [PATCH 341/438] Guard trace logging with isTraceEnabled Wrap the trace log call in ClassicProcessIdResolver#current() with logger.isTraceEnabled() to avoid unnecessary message construction when trace logging is disabled. Preserves existing behavior when trace is enabled while reducing potential performance overhead. --- .../java/io/microsphere/process/ClassicProcessIdResolver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ClassicProcessIdResolver.java b/microsphere-java-core/src/main/java/io/microsphere/process/ClassicProcessIdResolver.java index 10ff9f6ac..496914eab 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ClassicProcessIdResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ClassicProcessIdResolver.java @@ -82,7 +82,9 @@ public boolean supports() { @Override public Long current() { Long processId = valueOf(processIdValue); - logger.trace("The PID was resolved from the method 'java.lang.management.RuntimeMXBean#getName()' = {} : {}", runtimeName, processId); + if (logger.isTraceEnabled()) { + logger.trace("The PID was resolved from the method 'java.lang.management.RuntimeMXBean#getName()' = {} : {}", runtimeName, processId); + } return processId; } From 96e03aa918a7589a371b881db37ce355bb99a73a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:29:45 +0800 Subject: [PATCH 342/438] Make ClassicProcessIdResolverTest extend LoggingTest Update the test class to extend io.microsphere.LoggingTest and add the required import so the test can use shared logging utilities/capture. No behavior changes to test logic. --- .../io/microsphere/process/ClassicProcessIdResolverTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ClassicProcessIdResolverTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ClassicProcessIdResolverTest.java index 30cb457e4..4d4acb7a2 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ClassicProcessIdResolverTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ClassicProcessIdResolverTest.java @@ -1,5 +1,6 @@ package io.microsphere.process; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -14,7 +15,7 @@ * @see ClassicProcessIdResolver * @since 1.0.0 */ -class ClassicProcessIdResolverTest { +class ClassicProcessIdResolverTest extends LoggingTest { private ClassicProcessIdResolver resolver; From 28509827533ed0404bc8cf8484eb20538b6842e2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:30:13 +0800 Subject: [PATCH 343/438] Guard trace logging with isTraceEnabled Wrap the logger.trace call in ModernProcessIdResolver.current() with logger.isTraceEnabled() to avoid unnecessary logging overhead (message formatting) when TRACE is disabled. This reduces runtime cost for the PID resolution path. --- .../java/io/microsphere/process/ModernProcessIdResolver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ModernProcessIdResolver.java b/microsphere-java-core/src/main/java/io/microsphere/process/ModernProcessIdResolver.java index c1c606418..65dd726d3 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ModernProcessIdResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ModernProcessIdResolver.java @@ -64,7 +64,9 @@ public boolean supports() { public Long current() { Object processHandle = invokeStaticMethod(PROCESS_HANDLE_CLASS, "current"); Long pid = invokeMethod(processHandle, PROCESS_HANDLE_CLASS, "pid"); - logger.trace("The PID was resolved from the method 'java.lang.ProcessHandle#pid()' : {}", pid); + if (logger.isTraceEnabled()) { + logger.trace("The PID was resolved from the method 'java.lang.ProcessHandle#pid()' : {}", pid); + } return pid; } From ef201f98635c3b097da8e1c69d18dc656fce1c8b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:30:23 +0800 Subject: [PATCH 344/438] Extend ModernProcessIdResolverTest with LoggingTest Import io.microsphere.LoggingTest and make ModernProcessIdResolverTest extend LoggingTest. This enables shared logging setup/utility for the test class so test logging can be captured or configured consistently. --- .../io/microsphere/process/ModernProcessIdResolverTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ModernProcessIdResolverTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ModernProcessIdResolverTest.java index f98cb7a7b..8a82333e0 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ModernProcessIdResolverTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ModernProcessIdResolverTest.java @@ -1,5 +1,6 @@ package io.microsphere.process; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,7 +18,7 @@ * @see ModernProcessIdResolver * @since 1.0.0 */ -class ModernProcessIdResolverTest { +class ModernProcessIdResolverTest extends LoggingTest { private ModernProcessIdResolver resolver; From 62ee6a3197eabec6916943c4bb10a6b68827acf5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:30:53 +0800 Subject: [PATCH 345/438] Guard trace logging with isTraceEnabled() Wrap the logger.trace(...) call in ProcessExecutor with a logger.isTraceEnabled() check to avoid unnecessary message formatting when trace logging is disabled. The change sits in the finally block after removing unfinished processes and is a small performance optimization. --- .../src/main/java/io/microsphere/process/ProcessExecutor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java index 043b5d155..a21a695d2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/ProcessExecutor.java @@ -175,7 +175,9 @@ public void execute(OutputStream outputStream, long timeout, TimeUnit timeUnit) } } finally { processManager.removeUnfinishedProcess(process, options); - logger.trace("The command['{}'] is executed with exit value : {}", commandLine, exitValue); + if (logger.isTraceEnabled()) { + logger.trace("The command['{}'] is executed with exit value : {}", commandLine, exitValue); + } } return targetOutputStream.toByteArray(); }); From d2bba53243451c3359b98b84d2108398b50ac908 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:31:06 +0800 Subject: [PATCH 346/438] Make ProcessExecutorTest extend LoggingTest Add import for LoggingTest and change ProcessExecutorTest from implementing Loggable to extending LoggingTest (while still implementing Loggable). This lets the test reuse shared logging/test utilities (captures/initialization) when running process-related tests. --- .../test/java/io/microsphere/process/ProcessExecutorTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java index 0c81927b7..1c9f0ecbb 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/ProcessExecutorTest.java @@ -1,6 +1,7 @@ package io.microsphere.process; import io.microsphere.Loggable; +import io.microsphere.LoggingTest; import io.microsphere.io.FastByteArrayOutputStream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -20,7 +21,7 @@ * @see ProcessExecutorTest * @since 1.0.0 */ -class ProcessExecutorTest implements Loggable { +class ProcessExecutorTest extends LoggingTest implements Loggable { private FastByteArrayOutputStream outputStream; From f7d5611ee8eadec9d06e34b106ba50f002c19077 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:31:53 +0800 Subject: [PATCH 347/438] Guard trace logging with isTraceEnabled Wrap the trace logging call in a logger.isTraceEnabled() check to avoid unnecessary logging overhead when trace level is disabled. This change limits work done for the PID resolution log message in VirtualMachineProcessIdResolver. --- .../microsphere/process/VirtualMachineProcessIdResolver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java b/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java index a2e48bbba..de4c5bead 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/process/VirtualMachineProcessIdResolver.java @@ -90,7 +90,9 @@ public Long current() { RuntimeMXBean runtimeMXBean = getRuntimeMXBean(); Object jvm = getFieldValue(runtimeMXBean, JVM_FIELD); Integer processId = invokeMethod(jvm, GET_PROCESS_ID_METHOD_NAME); - logger.trace("The PID was resolved from the native method 'sun.management.VMManagementImpl#getProcessId()' : {}", processId); + if (logger.isTraceEnabled()) { + logger.trace("The PID was resolved from the native method 'sun.management.VMManagementImpl#getProcessId()' : {}", processId); + } return valueOf(processId.longValue()); } From 2c6e3e188a152e18911ba2dd4397ec9fa2a71097 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 13:32:07 +0800 Subject: [PATCH 348/438] Make VM process test extend LoggingTest Add LoggingTest support to VirtualMachineProcessIdResolverTest by importing io.microsphere.LoggingTest and changing the test class to extend LoggingTest. This enables logging capture/behavior provided by the base test class during the resolver tests. --- .../process/VirtualMachineProcessIdResolverTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/process/VirtualMachineProcessIdResolverTest.java b/microsphere-java-core/src/test/java/io/microsphere/process/VirtualMachineProcessIdResolverTest.java index f727f8a1c..c80f8828e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/process/VirtualMachineProcessIdResolverTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/process/VirtualMachineProcessIdResolverTest.java @@ -1,5 +1,6 @@ package io.microsphere.process; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -14,7 +15,7 @@ * @see VirtualMachineProcessIdResolver * @since 1.0.0 */ -class VirtualMachineProcessIdResolverTest { +class VirtualMachineProcessIdResolverTest extends LoggingTest { private VirtualMachineProcessIdResolver resolver; From 4cd9b50a829696238d778772d2c01fe915de8655 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 21:16:40 +0800 Subject: [PATCH 349/438] Add addIfAbsent to ListUtils Introduce addIfAbsent(List, T) utility method that adds an element only if it's not already present in the list. The method returns true when the element was added and false if the list already contained the value (uses contains() then add()). Provides a convenient way to avoid duplicates for list-based collections. --- .../src/main/java/io/microsphere/collection/ListUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/ListUtils.java b/microsphere-java-core/src/main/java/io/microsphere/collection/ListUtils.java index 88a357691..5bc2a60fc 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/collection/ListUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/ListUtils.java @@ -668,6 +668,10 @@ public static void forEach(List values, Consumer consumer) { forEach(values, (i, e) -> consumer.accept(e)); } + public static boolean addIfAbsent(List values, T newValue) { + return values.contains(newValue) ? false : values.add(newValue); + } + private ListUtils() { } } From 72b50285a1ece4da8634ec94100391d8d559a621 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 21:18:02 +0800 Subject: [PATCH 350/438] Add test for ListUtils.addIfAbsent Add a static import and a new unit test (testAddIfAbsent) in ListUtilsTest to verify ListUtils.addIfAbsent behavior: it returns true when adding a new element, false when adding a duplicate, and true when adding another distinct element. --- .../java/io/microsphere/collection/ListUtilsTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java index adb75ead1..a51411614 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ListUtilsTest.java @@ -30,6 +30,7 @@ import static io.microsphere.AbstractTestCase.TEST_NULL_ITERABLE; import static io.microsphere.AbstractTestCase.TEST_NULL_ITERATOR; import static io.microsphere.AbstractTestCase.TEST_NULL_LIST; +import static io.microsphere.collection.ListUtils.addIfAbsent; import static io.microsphere.collection.ListUtils.first; import static io.microsphere.collection.ListUtils.forEach; import static io.microsphere.collection.ListUtils.isList; @@ -204,4 +205,12 @@ void testForEach() { assertEquals(iterator2.next(), value); }); } + + @Test + void testAddIfAbsent() { + List values = newArrayList(); + assertTrue(addIfAbsent(values, "A")); + assertFalse(addIfAbsent(values, "A")); + assertTrue(addIfAbsent(values, "B")); + } } \ No newline at end of file From 28c3398d0e94f3026366e697caa670680a9cd30d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 21:18:20 +0800 Subject: [PATCH 351/438] Use addIfAbsent and length util in TypeFinder Import ListUtils.addIfAbsent and ArrayUtils.length, replace manual array length checks and explicit contains()-then-add patterns with addIfAbsent for interface and supertype insertion, and use length(...) for array sizing. Also simplify superclass addition and perform minor cleanup (removed trailing newline). Improves readability and centralizes duplicate-check logic. --- .../java/io/microsphere/util/TypeFinder.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/TypeFinder.java b/microsphere-java-core/src/main/java/io/microsphere/util/TypeFinder.java index 66c21dcf2..438f8ae20 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/TypeFinder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/TypeFinder.java @@ -24,6 +24,7 @@ import java.util.function.Function; import java.util.function.Predicate; +import static io.microsphere.collection.ListUtils.addIfAbsent; import static io.microsphere.collection.ListUtils.newArrayList; import static io.microsphere.collection.ListUtils.newLinkedList; import static io.microsphere.lang.function.Predicates.EMPTY_PREDICATE_ARRAY; @@ -34,6 +35,7 @@ import static io.microsphere.util.ArrayUtils.EMPTY_TYPE_ARRAY; import static io.microsphere.util.ArrayUtils.contains; import static io.microsphere.util.ArrayUtils.isNotEmpty; +import static io.microsphere.util.ArrayUtils.length; import static io.microsphere.util.Assert.assertNoNullElements; import static io.microsphere.util.Assert.assertNotEmpty; import static io.microsphere.util.Assert.assertNotNull; @@ -169,7 +171,7 @@ protected List getSuperTypes(T type, boolean includeSuperclass, boolean inclu } T[] interfaceTypes = includedGenericInterfaces ? getInterfaces(type) : (T[]) EMPTY_TYPE_ARRAY; - int interfaceTypesLength = interfaceTypes.length; + int interfaceTypesLength = length(interfaceTypes); int size = interfaceTypesLength + (hasSuperclass ? 1 : 0); @@ -180,16 +182,12 @@ protected List getSuperTypes(T type, boolean includeSuperclass, boolean inclu List types = newArrayList(size); if (hasSuperclass) { - if (!types.contains(superclass)) { - types.add(superclass); - } + types.add(superclass); } for (int i = 0; i < interfaceTypesLength; i++) { T interfaceType = interfaceTypes[i]; - if (!types.contains(interfaceType)) { - types.add(interfaceType); - } + addIfAbsent(types, interfaceType); } return types; } @@ -225,9 +223,8 @@ protected void addSuperTypes(List allTypes, T type, boolean includeHierarchic for (int i = 0; i < superTypesSize; i++) { T superType = superTypes.get(i); - if (!allTypes.contains(superType)) { - allTypes.add(superType); - } + addIfAbsent(allTypes, superType); + if (includeHierarchicalTypes) { addSuperTypes(allTypes, superType, true, includeSuperclass, includeInterfaces); } @@ -273,5 +270,4 @@ public static TypeFinder genericTypeFinder(Type type, boolean includeSelf, return new TypeFinder(type, genericTypeGetSuperClassFunction, genericTypeGetInterfacesFunction, includeSelf, includeHierarchicalTypes, includeSuperclass, includeInterfaces); } - -} +} \ No newline at end of file From cf531eb60163bf8e32e7f5c6f70b5beb6454761a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 21:18:34 +0800 Subject: [PATCH 352/438] Add tests for TypeFinder.getSuperTypes Add unit tests verifying TypeFinder.getSuperTypes behavior: testGetSuperTypes checks resolution of String's supertypes (Object, Serializable, Comparable) using classGetSuperClassFunction; testGetSuperTypesWithNullType verifies that passing null returns an emptyList for various include flags. Also add required imports and static imports (Serializable, ofList, ofArray, classGetSuperClassFunction) to support the new tests. --- .../io/microsphere/util/TypeFinderTest.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java index fa0ed880d..84390936f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java @@ -8,6 +8,7 @@ import io.microsphere.util.TypeFinder.Include; import org.junit.jupiter.api.Test; +import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -15,12 +16,15 @@ import java.util.function.BiFunction; import static io.microsphere.AbstractTestCase.assertValues; +import static io.microsphere.collection.Lists.ofList; import static io.microsphere.reflect.TypeUtils.NON_OBJECT_TYPE_FILTER; +import static io.microsphere.util.ArrayUtils.ofArray; import static io.microsphere.util.TypeFinder.Include.HIERARCHICAL; import static io.microsphere.util.TypeFinder.Include.INTERFACES; import static io.microsphere.util.TypeFinder.Include.SUPER_CLASS; import static io.microsphere.util.TypeFinder.Include.values; import static io.microsphere.util.TypeFinder.classFinder; +import static io.microsphere.util.TypeFinder.classGetSuperClassFunction; import static io.microsphere.util.TypeFinder.genericTypeFinder; import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -33,7 +37,6 @@ * {@link TypeFinder} Test * * @author Mercy - * @see io.microsphere.util.ClassFinder * @since 1.0.0 */ class TypeFinderTest { @@ -187,6 +190,30 @@ void testFindAllGenericClasses() { assertGenericInterfaces(types); } + @Test + void testGetSuperTypes() { + TypeFinder> typeFinder = new TypeFinder(String.class, classGetSuperClassFunction, + t -> ofArray(Serializable.class, Serializable.class, Comparable.class), true, true, true, true); + List> superTypes = typeFinder.getSuperTypes(String.class, true, true); + assertEquals(ofList(Object.class, Serializable.class, Comparable.class), superTypes); + } + + @Test + void testGetSuperTypesWithNullType() { + TypeFinder typeFinder = genericTypeFinder(StringIntegerToBooleanClass.class, values()); + List superTypes = typeFinder.getSuperTypes(null, true, true); + assertSame(emptyList(), superTypes); + + superTypes = typeFinder.getSuperTypes(null, false, true); + assertSame(emptyList(), superTypes); + + superTypes = typeFinder.getSuperTypes(null, false, false); + assertSame(emptyList(), superTypes); + + superTypes = typeFinder.getSuperTypes(null, true, false); + assertSame(emptyList(), superTypes); + } + private void assertGenericInterfaces(List types) { assertEquals(StringIntegerToBoolean.class, types.get(types.size() - 5)); assertStringIntegerF1(types.get(types.size() - 4)); From db49d6b4968e2f07ff1d8db2e999e346d1a3d439 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Feb 2026 21:39:08 +0800 Subject: [PATCH 353/438] Add tests for generic type helpers Add unit tests for TypeFinder utilities: verify genericTypeGetSuperClassFunction and genericTypeGetInterfacesFunction return null for null input, and test addSuperTypes behavior (no additions when flags are false, and additions when interfaces/superclasses are enabled). Also add necessary static imports and assertions used by the new tests. --- .../io/microsphere/util/TypeFinderTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java index 84390936f..5f461ce59 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/TypeFinderTest.java @@ -16,6 +16,7 @@ import java.util.function.BiFunction; import static io.microsphere.AbstractTestCase.assertValues; +import static io.microsphere.collection.ListUtils.newArrayList; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.reflect.TypeUtils.NON_OBJECT_TYPE_FILTER; import static io.microsphere.util.ArrayUtils.ofArray; @@ -26,9 +27,13 @@ import static io.microsphere.util.TypeFinder.classFinder; import static io.microsphere.util.TypeFinder.classGetSuperClassFunction; import static io.microsphere.util.TypeFinder.genericTypeFinder; +import static io.microsphere.util.TypeFinder.genericTypeGetInterfacesFunction; +import static io.microsphere.util.TypeFinder.genericTypeGetSuperClassFunction; import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -214,6 +219,27 @@ void testGetSuperTypesWithNullType() { assertSame(emptyList(), superTypes); } + @Test + void testGenericTypeGetSuperClassFunctionWithNull() { + assertNull(genericTypeGetSuperClassFunction.apply(null)); + } + + @Test + void testGenericTypeGetInterfacesFunction() { + assertNull(genericTypeGetInterfacesFunction.apply(null)); + } + + @Test + void testAddSuperTypes() { + TypeFinder typeFinder = genericTypeFinder(String.class, values()); + List types = newArrayList(); + typeFinder.addSuperTypes(types, String.class, false, false, false); + assertTrue(types.isEmpty()); + + typeFinder.addSuperTypes(types, String.class, false, true, true); + assertFalse(types.isEmpty()); + } + private void assertGenericInterfaces(List types) { assertEquals(StringIntegerToBoolean.class, types.get(types.size() - 5)); assertStringIntegerF1(types.get(types.size() - 4)); From 6d572b247a38ba4bd02242c4b8dc28fad710096f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 27 Feb 2026 11:06:42 +0800 Subject: [PATCH 354/438] Update FieldUtils.java --- .../io/microsphere/reflect/FieldUtils.java | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java index 3e5bcafce..da327d5b2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/FieldUtils.java @@ -509,8 +509,6 @@ public static V getFieldValue(Object instance, String fieldName, Class fi * @param instance The object instance from which to retrieve the field value * @param field The {@link Field} object representing the field to retrieve * @return The value of the field if found and accessible; otherwise, {@code null} - * @throws IllegalStateException if this Field object is enforcing Java language access control and the underlying - * field is inaccessible * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring * the underlying field (or a subclass or implementor thereof) */ @@ -525,10 +523,9 @@ public static V getFieldValue(Object instance, Field field) throws IllegalSt try { accessible = trySetAccessible(field); fieldValue = (V) field.get(instance); - } catch (IllegalAccessException e) { - handleIllegalAccessException(e, instance, field, accessible); - } catch (IllegalArgumentException e) { - handleIllegalArgumentException(e, instance, field); + } catch (IllegalAccessException | IllegalArgumentException e) { + handleFieldException(e, instance, field); + throw new IllegalArgumentException(e); } return fieldValue; @@ -612,7 +609,6 @@ public static V setFieldValue(Object instance, String fieldName, V value) th * @param field The {@link Field} object representing the field to modify * @param value The new value to assign to the field * @return The previous value of the field before modification, or {@code null} if the field was not found or inaccessible - * @throws IllegalStateException If this Field object is enforcing Java language access control and the underlying field is inaccessible * @throws IllegalArgumentException If the specified object is not an instance of the class or interface declaring the underlying field */ @Nullable @@ -622,17 +618,15 @@ public static V setFieldValue(Object instance, Field field, V value) throws } V previousValue = null; - boolean accessible = false; try { - accessible = trySetAccessible(field); + trySetAccessible(field); previousValue = (V) field.get(instance); if (!Objects.equals(previousValue, value)) { field.set(instance, value); } - } catch (IllegalAccessException e) { - handleIllegalAccessException(e, instance, field, accessible); - } catch (IllegalArgumentException e) { - handleIllegalArgumentException(e, instance, field); + } catch (IllegalAccessException | IllegalArgumentException e) { + handleFieldException(e, instance, field); + throw new IllegalArgumentException(e); } return previousValue; @@ -681,22 +675,11 @@ public static void assertFieldMatchType(Object instance, String fieldName, Class } } - static void handleIllegalAccessException(IllegalAccessException e, Object instance, Field field, boolean accessible) { - String errorMessage = format("The instance [object : {} , class : {} ] can't access the field[name : '{}' , type : {} , accessible : {}]", - instance, getTypeName(instance.getClass()), field.getName(), getTypeName(field.getType()), accessible); - if (logger.isTraceEnabled()) { - logger.trace(errorMessage); - } - throw new IllegalStateException(errorMessage, e); - } - - static void handleIllegalArgumentException(IllegalArgumentException e, Object instance, Field field) { - String errorMessage = format("The instance[object : {} , class : {}] can't match the field[name : '{}' , type : {}]", - instance, getTypeName(instance.getClass()), field.getName(), getTypeName(field.getType())); + static void handleFieldException(Exception e, Object instance, Field field) { if (logger.isTraceEnabled()) { - logger.trace(errorMessage); + logger.trace("The instance[object : {} , class : {}] can't match the field[name : '{}' , type : {}]", + instance, getTypeName(instance.getClass()), field.getName(), getTypeName(field.getType()), e); } - throw new IllegalArgumentException(errorMessage, e); } private FieldUtils() { From 42f38c37a4c26a1a4655ad3fbd670ce6e8bda8f4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 27 Feb 2026 11:06:57 +0800 Subject: [PATCH 355/438] Use handleFieldException in FieldUtilsTest Update FieldUtilsTest to use the new FieldUtils.handleFieldException API. Replaced the static import of handleIllegalAccessException with handleFieldException, added assertDoesNotThrow import, renamed the test to testHandleFieldException, and adjusted the invocation to match the new method signature (removed the accessibility argument). This reflects an API change in FieldUtils. --- .../test/java/io/microsphere/reflect/FieldUtilsTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java index 3b507864a..4dbc8c77a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/FieldUtilsTest.java @@ -30,12 +30,13 @@ import static io.microsphere.reflect.FieldUtils.getDeclaredField; import static io.microsphere.reflect.FieldUtils.getFieldValue; import static io.microsphere.reflect.FieldUtils.getStaticFieldValue; -import static io.microsphere.reflect.FieldUtils.handleIllegalAccessException; +import static io.microsphere.reflect.FieldUtils.handleFieldException; import static io.microsphere.reflect.FieldUtils.setFieldValue; import static io.microsphere.reflect.FieldUtils.setStaticFieldValue; import static io.microsphere.util.VersionUtils.CURRENT_JAVA_VERSION; import static io.microsphere.util.VersionUtils.JAVA_VERSION_8; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -263,9 +264,9 @@ void testAssertFieldMatchTypeOnIllegalArgumentException() { } @Test - void testHandleIllegalAccessException() { + void testHandleFieldException() { Field field = findField(ReflectionTest.class, "staticField"); - assertThrows(IllegalStateException.class, () -> handleIllegalAccessException(new IllegalAccessException(), test, field, field.isAccessible())); + assertDoesNotThrow(() -> handleFieldException(new IllegalAccessException(), test, field)); } private void assertFindField(Object object, String fieldName) { From 9507cf4fe5ee20c1f1e239aa0501d94738ca933f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 27 Feb 2026 11:19:04 +0800 Subject: [PATCH 356/438] Test JSONArray(JSONTokener) constructor Add a unit test for the JSONArray(JSONTokener) constructor in JSONArrayTest. Adds a static import for assertDoesNotThrow and a test method that asserts a JSONTokener with '[1,2,3]' does not throw, while a JSONTokener with '{}' results in a JSONException. --- .../src/test/java/io/microsphere/json/JSONArrayTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/json/JSONArrayTest.java b/microsphere-java-core/src/test/java/io/microsphere/json/JSONArrayTest.java index 8b5a6ab8f..4947aa4b4 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/json/JSONArrayTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/json/JSONArrayTest.java @@ -30,6 +30,7 @@ import static io.microsphere.util.ArrayUtils.ofArray; import static java.lang.Boolean.TRUE; import static java.lang.Double.NaN; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -117,6 +118,12 @@ void testConstructorWithArrayOnNotArray() { assertThrows(JSONException.class, () -> new JSONArray(new Object())); } + @Test + void testConstructorWithJSONTokener() { + assertDoesNotThrow(() -> new JSONArray(new JSONTokener("[1,2,3]"))); + assertThrows(JSONException.class, () -> new JSONArray(new JSONTokener("{}"))); + } + @Test void testLength() { assertEquals(0, jsonArray.length()); From 1b42d0049c4d5f53ac4b63cb958bf6945364aa87 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 27 Feb 2026 11:38:27 +0800 Subject: [PATCH 357/438] Remove redundant 'this.' in recursive scan call In SimpleFileScanner, replace `this.scan(subFile, recursive, ioFileFilter)` with `scan(subFile, recursive, ioFileFilter)` to remove an unnecessary qualifier. This is a minor stylistic cleanup with no functional change. --- .../main/java/io/microsphere/io/scanner/SimpleFileScanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleFileScanner.java b/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleFileScanner.java index 6356cc99a..48adfa663 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleFileScanner.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleFileScanner.java @@ -100,7 +100,7 @@ public Set scan(File rootDirectory, boolean recursive, IOFileFilter ioFile filesSet.add(subFile); } if (recursive && subFile.isDirectory()) { - filesSet.addAll(this.scan(subFile, recursive, ioFileFilter)); + filesSet.addAll(scan(subFile, recursive, ioFileFilter)); } } } From d6ee1e6236c555f4692f51f611c48282462fd4d3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 27 Feb 2026 11:38:33 +0800 Subject: [PATCH 358/438] Add tests for scanning empty directory and file Add two unit tests to SimpleFileScannerTest: one verifies scanning an empty temporary directory returns that directory, and the other verifies scanning a temporary file returns that file. Also add imports for helper methods (createRandomTempDirectory/createRandomTempFile), IOException, and assertTrue. --- .../io/scanner/SimpleFileScannerTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleFileScannerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleFileScannerTest.java index e8209df7e..9c84641a2 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleFileScannerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleFileScannerTest.java @@ -8,12 +8,16 @@ import org.junit.jupiter.api.Test; import java.io.File; +import java.io.IOException; import java.util.Set; +import static io.microsphere.AbstractTestCase.createRandomTempDirectory; +import static io.microsphere.AbstractTestCase.createRandomTempFile; import static io.microsphere.io.scanner.SimpleFileScanner.INSTANCE; import static io.microsphere.util.SystemUtils.JAVA_HOME; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link SimpleFileScanner} {@link Test} @@ -48,4 +52,20 @@ void testScanOnBinDirectory() { Set directories = simpleFileScanner.scan(JAVA_HOME_DIR, true, new NameFileFilter("bin")); assertEquals(1, directories.size()); } + + @Test + void testScanOnEmptyDirectory() { + File tempDir = createRandomTempDirectory(); + Set directories = simpleFileScanner.scan(tempDir, true); + assertEquals(1, directories.size()); + assertTrue(directories.contains(tempDir)); + } + + @Test + void testScanOnFile() throws IOException { + File file = createRandomTempFile(); + Set files = simpleFileScanner.scan(file, true); + assertEquals(1, files.size()); + assertTrue(files.contains(file)); + } } \ No newline at end of file From 55f94b1ab779e0334edab7e279013d034bc1ccf5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 11:58:29 +0800 Subject: [PATCH 359/438] Add StackWalker & MethodHandle caller lookup Introduce JDK9+ StackWalker support and MethodHandle usage for sun.reflect.Reflection#getCallerClass. This change: - Adds optional STACK_WALKER and STACK_WALKER_STACK_FRAME class resolution and initializes a StackWalker instance for efficient frame inspection. - Replaces direct reflective invocation of sun.reflect.Reflection#getCallerClass with a MethodHandle (findPublicStatic) and prefers it when available. - Adds new helper paths to obtain caller class names: prefer sun.reflect Reflection, then StackWalker, then fall back to stack trace. - Refactors and consolidates multiple getCallerClass/getCallerClassName implementations and related invocation-frame calculations. - Updates imports to use MethodHandle, Streams, and MethodUtils helpers and removes some older reflection usages. These changes improve compatibility with newer JDKs (noisy reflective access / InaccessibleObjectException) and provide a more robust, layered strategy for determining caller classes. --- .../microsphere/reflect/ReflectionUtils.java | 270 ++++++++++++------ 1 file changed, 183 insertions(+), 87 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java index f5bf4c3ca..19b124b20 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java @@ -7,6 +7,7 @@ import io.microsphere.logging.Logger; import io.microsphere.util.Utils; +import java.lang.invoke.MethodHandle; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -14,17 +15,24 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static io.microsphere.collection.MapUtils.newLinkedHashMap; +import static io.microsphere.invoke.MethodHandlesLookupUtils.findPublicStatic; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.reflect.FieldUtils.getFieldValue; import static io.microsphere.reflect.MemberUtils.isStatic; +import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.reflect.MethodUtils.invokeMethod; +import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; import static io.microsphere.reflect.TypeUtils.getTypeName; import static io.microsphere.util.ClassLoaderUtils.resolveClass; import static io.microsphere.util.ClassUtils.getType; import static io.microsphere.util.ClassUtils.isPrimitive; import static io.microsphere.util.ClassUtils.isSimpleType; -import static java.lang.Class.forName; +import static io.microsphere.util.StackTraceUtils.getStackTrace; import static java.lang.Thread.currentThread; import static java.lang.reflect.Array.get; import static java.lang.reflect.Array.getLength; @@ -59,11 +67,23 @@ public abstract class ReflectionUtils implements Utils { */ public static final String SUN_REFLECT_REFLECTION_CLASS_NAME = "sun.reflect.Reflection"; + /** + * The {@link Class} of sun.reflect.Reflection + */ + @Nullable + public static final Class SUN_REFLECT_REFLECTION_CLASS = resolveClass(SUN_REFLECT_REFLECTION_CLASS_NAME); + /** * sun.reflect.Reflection method name */ private static final String getCallerClassMethodName = "getCallerClass"; + /** + * The {@link MethodHandle} of Reflection#getCallerClass(int) + */ + @Nullable + private static final MethodHandle getCallerClassMethodHandle = findPublicStatic(SUN_REFLECT_REFLECTION_CLASS, getCallerClassMethodName, int.class); + /** * sun.reflect.Reflection invocation frame */ @@ -77,12 +97,60 @@ public abstract class ReflectionUtils implements Utils { /** * Is Supported sun.reflect.Reflection ? */ - private static final boolean supportedSunReflectReflection; + private static final boolean supportedSunReflectReflection = getCallerClassMethodHandle != null; + + /** + * The class name of {@linkplain java.lang.StackWalker} that was introduced in JDK 9. + */ + public static final String STACK_WALKER_CLASS_NAME = "java.lang.StackWalker"; + + /** + * The class name of {@linkplain java.lang.StackWalker.StackFrame} that was introduced in JDK 9. + */ + public static final String STACK_WALKER_STACK_FRAME_CLASS_NAME = "java.lang.StackWalker$StackFrame"; + + /** + * The {@link Class} of {@linkplain java.lang.StackWalker} that was introduced in JDK 9. + * (optional) + */ + @Nullable + public static final Class STACK_WALKER_CLASS = resolveClass(STACK_WALKER_CLASS_NAME); + + /** + * The {@link Class} of {@linkplain java.lang.StackWalker.StackFrame} that was introduced in JDK 9. + * (optional) + */ + @Nullable + public static final Class STACK_WALKER_STACK_FRAME_CLASS = resolveClass(STACK_WALKER_STACK_FRAME_CLASS_NAME); + + /** + * The {@link Method method} name of {@linkplain java.lang.StackWalker#getInstance()} + */ + static final String GET_INSTANCE_METHOD_NAME = "getInstance"; /** - * sun.reflect.Reflection#getCallerClass(int) method + * The {@link Method method} name of {{@linkplain java.lang.StackWalker#walk(java.util.function.Function)} */ - private static final Method getCallerClassMethod; + static final String WALK_METHOD_NAME = "walk"; + + /** + * The {@link Method method} name of {@linkplain java.lang.StackWalker.StackFrame#getClassName()} + */ + static final String GET_CLASS_NAME_METHOD_NAME = "getClassName"; + + static final Method WALK_METHOD = findMethod(STACK_WALKER_CLASS, WALK_METHOD_NAME, Function.class); + + static final Method GET_CLASS_NAME_METHOD = findMethod(STACK_WALKER_STACK_FRAME_CLASS, GET_CLASS_NAME_METHOD_NAME); + + @Nullable + private static Object stackWalkerInstance; + + /** + * {@linkplain java.lang.StackWalker} invocation frame. + */ + private static final int stackWalkerInvocationFrame; + + private static final Function, Object> getClassNamesFunction = ReflectionUtils::getCallerClassNamesInStackWalker; /** * The class name of {@linkplain java.lang.reflect.InaccessibleObjectException} since JDK 9 @@ -96,40 +164,44 @@ public abstract class ReflectionUtils implements Utils { @Nullable public static final Class INACCESSIBLE_OBJECT_EXCEPTION_CLASS = (Class) resolveClass(INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME); + // Initialize java.lang.StackWalker + static { + int invocationFrame = 0; + if (STACK_WALKER_CLASS != null) { + stackWalkerInstance = invokeStaticMethod(STACK_WALKER_CLASS, GET_INSTANCE_METHOD_NAME); + List stackFrameClassNames = getCallerClassNamesInStackWalker(); + for (String stackFrameClassName : stackFrameClassNames) { + if (TYPE.getName().equals(stackFrameClassName)) { + break; + } + invocationFrame++; + } + } + stackWalkerInvocationFrame = invocationFrame + 2; + } + // Initialize sun.reflect.Reflection static { - Method method = null; - boolean supported = false; int invocationFrame = 0; - try { - // Use sun.reflect.Reflection to calculate frame - Class type = forName(SUN_REFLECT_REFLECTION_CLASS_NAME); - method = type.getMethod(getCallerClassMethodName, int.class); - method.setAccessible(true); - // Adapt SUN JDK ,The value of invocation frame in JDK 6/7/8 may be different + if (supportedSunReflectReflection) { + // Adapt SUN JDK ,The value of invocation frame in JDK 7/8 may be different for (int i = 0; i < 9; i++) { - Class callerClass = (Class) method.invoke(null, i); + Class callerClass = getCallerClassInSunReflectReflection(i); if (TYPE.equals(callerClass)) { invocationFrame = i; break; } } - supported = true; - } catch (Throwable e) { - if (logger.isTraceEnabled()) { - logger.trace("The class '{}' or its' method '{}({})' can't be initialized.", SUN_REFLECT_REFLECTION_CLASS_NAME, getCallerClassMethodName, int.class, e); - } } + // set method info - getCallerClassMethod = method; - supportedSunReflectReflection = supported; // getCallerClass() -> getCallerClass(int) // Plugs 1 , because Invocation getCallerClass() method was considered as increment invocation frame // Plugs 1 , because Invocation getCallerClass(int) method was considered as increment invocation frame sunReflectReflectionInvocationFrame = invocationFrame + 2; } - // Initialize StackTraceElement + // Initialize java.lang.StackTraceElement static { int invocationFrame = 0; // Use java.lang.StackTraceElement to calculate frame @@ -141,10 +213,7 @@ public abstract class ReflectionUtils implements Utils { } invocationFrame++; } - // getCallerClass() -> getCallerClass(int) - // Plugs 1 , because Invocation getCallerClass() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClass(int) method was considered as increment invocation frame - stackTraceElementInvocationFrame = invocationFrame + 2; + stackTraceElementInvocationFrame = invocationFrame; } /** @@ -198,21 +267,56 @@ public static boolean isSupportedSunReflectReflection() { */ @Nonnull public static String getCallerClassName() { - if (supportedSunReflectReflection) { - Class callerClass = getCallerClassInSunJVM(sunReflectReflectionInvocationFrame); - if (callerClass != null) return callerClass.getName(); + Class callerClass = getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrame); + return callerClass == null ? getCallerClassName(stackWalkerInstance, 1) : callerClass.getName(); + } + + @Nullable + static String getCallerClassName(Object stackWalkerInstance, int frameOffSet) { + if (stackWalkerInstance == null) { + // Plugs 1 , because Invocation getStackTrace() method was considered as increment invocation frame + // Plugs 1 , because Invocation getCallerClassName() method was considered as increment invocation frame + // Plugs 1 , because Invocation getCallerClassNameInStackTrace(int) method was considered as increment invocation frame + return getCallerClassNameInStackTrace(stackTraceElementInvocationFrame + 3 + frameOffSet); } - return getCallerClassNameInGeneralJVM(stackTraceElementInvocationFrame); + List callerClassNames = getCallerClassNamesInStackWalker(stackWalkerInstance); + int frame = stackWalkerInvocationFrame + frameOffSet; + if (frame < callerClassNames.size()) { + return callerClassNames.get(frame); + } + return null; + } + + @Nonnull + static List getCallerClassNamesInStackWalker(@Nonnull Object stackWalkerInstance) { + return invokeMethod(stackWalkerInstance, WALK_METHOD, getClassNamesFunction); + } + + static List getCallerClassNamesInStackWalker() { + return invokeMethod(stackWalkerInstance, WALK_METHOD, getClassNamesFunction); + } + + private static List getCallerClassNamesInStackWalker(Stream stackFrames) { + return stackFrames.limit(5) + .map(ReflectionUtils::getClassName) + .collect(Collectors.toList()); + } + + private static String getClassName(Object stackFrame) { + return invokeMethod(stackFrame, GET_CLASS_NAME_METHOD); } /** * General implementation, get the calling class name * * @return call class name - * @see #getCallerClassNameInGeneralJVM(int) + * @see #getCallerClassNameInStackTrace(int) */ - static String getCallerClassNameInGeneralJVM() { - return getCallerClassNameInGeneralJVM(stackTraceElementInvocationFrame); + static String getCallerClassNameInStackTrace() { + // Plugs 1 , because Invocation getStackTrace() method was considered as increment invocation frame + // Plugs 1 , because Invocation getCallerClassNameInStackTrace() method was considered as increment invocation frame + // Plugs 1 , because Invocation getCallerClassNameInStackTrace(int) method was considered as increment invocation frame + return getCallerClassNameInStackTrace(stackTraceElementInvocationFrame + 3); } /** @@ -221,8 +325,8 @@ static String getCallerClassNameInGeneralJVM() { * @param invocationFrame invocation frame * @return specified invocation frame class */ - static String getCallerClassNameInGeneralJVM(int invocationFrame) throws IndexOutOfBoundsException { - StackTraceElement[] elements = currentThread().getStackTrace(); + static String getCallerClassNameInStackTrace(int invocationFrame) throws IndexOutOfBoundsException { + StackTraceElement[] elements = getStackTrace(); if (invocationFrame < elements.length) { StackTraceElement targetStackTraceElement = elements[invocationFrame]; return targetStackTraceElement.getClassName(); @@ -230,37 +334,6 @@ static String getCallerClassNameInGeneralJVM(int invocationFrame) throws IndexOu return null; } - static Class getCallerClassInSunJVM(int realFramesToSkip) throws UnsupportedOperationException { - if (!supportedSunReflectReflection) { - throw new UnsupportedOperationException("Requires SUN's JVM!"); - } - Class callerClass = null; - if (getCallerClassMethod != null) { - try { - callerClass = (Class) getCallerClassMethod.invoke(null, realFramesToSkip); - } catch (Exception ignored) { - } - } - return callerClass; - } - - /** - * Get caller class in General JVM - * - * @param invocationFrame invocation frame - * @return caller class - * @see #getCallerClassNameInGeneralJVM(int) - */ - static Class getCallerClassInGeneralJVM(int invocationFrame) { - String className = getCallerClassNameInGeneralJVM(invocationFrame + 1); - Class targetClass = null; - try { - targetClass = className == null ? null : forName(className); - } catch (Throwable ignored) { - } - return targetClass; - } - /** * Gets the {@link Class} of the method caller. * @@ -284,24 +357,46 @@ static Class getCallerClassInGeneralJVM(int invocationFrame) { */ @Nonnull public static Class getCallerClass() throws IllegalStateException { + Class callerClass = getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrame); + if (callerClass != null) { + return callerClass; + } + String className = getCallerClassName(stackWalkerInstance, 1); + return resolveClass(className); + } + + @Nullable + static Class getCallerClassInSunReflectReflection(int realFramesToSkip) { if (supportedSunReflectReflection) { - Class callerClass = getCallerClassInSunJVM(sunReflectReflectionInvocationFrame); - if (callerClass != null) { - return callerClass; + try { + return (Class) getCallerClassMethodHandle.invokeExact(realFramesToSkip); + } catch (Throwable ignored) { } } - return getCallerClassInGeneralJVM(stackTraceElementInvocationFrame); + return null; + } + + /** + * Get caller class in General JVM + * + * @param invocationFrame invocation frame + * @return caller class + * @see #getCallerClassNameInStackTrace(int) + */ + static Class getCallerClassInStatckTrace(int invocationFrame) { + String className = getCallerClassNameInStackTrace(invocationFrame + 1); + return className == null ? null : resolveClass(className); } /** * Get caller class In SUN HotSpot JVM * * @return Caller Class - * @throws UnsupportedOperationException If JRE is not a SUN HotSpot JVM - * @see #getCallerClassInSunJVM(int) + * @see #getCallerClassInSunReflectReflection(int) */ - static Class getCallerClassInSunJVM() throws UnsupportedOperationException { - return getCallerClassInSunJVM(sunReflectReflectionInvocationFrame); + @Nullable + static Class getCallerClassInSunReflectReflection() throws UnsupportedOperationException { + return getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrame); } /** @@ -309,11 +404,12 @@ static Class getCallerClassInSunJVM() throws UnsupportedOperationException { * * @return Caller Class * @throws UnsupportedOperationException If JRE is not a SUN HotSpot JVM - * @see #getCallerClassInSunJVM(int) + * @see #getCallerClassInSunReflectReflection(int) */ - static String getCallerClassNameInSunJVM() throws UnsupportedOperationException { - Class callerClass = getCallerClassInSunJVM(sunReflectReflectionInvocationFrame); - return callerClass.getName(); + @Nullable + static String getCallerClassNameInSunReflectReflection() throws UnsupportedOperationException { + Class callerClass = getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrame); + return callerClass == null ? null : callerClass.getName(); } /** @@ -340,24 +436,24 @@ static String getCallerClassNameInSunJVM() throws UnsupportedOperationException * @return The class of the caller at the specified invocation frame. * @throws IllegalStateException if an error occurs while determining the caller class. */ + @Nullable public static Class getCallerClass(int invocationFrame) { - if (supportedSunReflectReflection) { - Class callerClass = getCallerClassInSunJVM(invocationFrame + 1); - if (callerClass != null) { - return callerClass; - } + Class callerClass = getCallerClassInSunReflectReflection(invocationFrame + 1); + if (callerClass != null) { + return callerClass; } - return getCallerClassInGeneralJVM(invocationFrame + 1); + String className = getCallerClassName(stackWalkerInstance, invocationFrame + 1); + return resolveClass(className); } /** - * Get caller class in General JVM + * Get caller class from {@link Thread#getStackTrace() stack traces} * * @return Caller Class - * @see #getCallerClassInGeneralJVM(int) + * @see #getCallerClassInStatckTrace(int) */ - static Class getCallerClassInGeneralJVM() { - return getCallerClassInGeneralJVM(stackTraceElementInvocationFrame); + static Class getCallerClassInStatckTrace() { + return getCallerClassInStatckTrace(stackTraceElementInvocationFrame + 3); } /** From f4b3788ef956b297ad0fc647db6244b58950a197 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 11:58:38 +0800 Subject: [PATCH 360/438] Enhance ReflectionUtilsTest for StackWalker Refactor and expand ReflectionUtilsTest to exercise new caller-resolution strategies (StackTrace/StackWalker and sun.reflect reflection). Introduces CALLER_CLASS/CALLER_CLASS_NAME constants, replaces older GeneralJVM/SunJVM assertions with StackTrace/StackWalker variants, and adds tests for frame bounds, StackWalker behavior across Java versions (using VersionUtils), and expected exceptions on unsupported JVMs. Also adjusts imports and uses assertThrows where appropriate. --- .../reflect/ReflectionUtilsTest.java | 68 ++++++++++++++----- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java index 3b3d1f8dd..a82e07686 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java @@ -11,21 +11,25 @@ import static io.microsphere.reflect.ReflectionUtils.INACCESSIBLE_OBJECT_EXCEPTION_CLASS; import static io.microsphere.reflect.ReflectionUtils.INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME; import static io.microsphere.reflect.ReflectionUtils.getCallerClass; -import static io.microsphere.reflect.ReflectionUtils.getCallerClassInGeneralJVM; -import static io.microsphere.reflect.ReflectionUtils.getCallerClassInSunJVM; +import static io.microsphere.reflect.ReflectionUtils.getCallerClassInStatckTrace; +import static io.microsphere.reflect.ReflectionUtils.getCallerClassInSunReflectReflection; import static io.microsphere.reflect.ReflectionUtils.getCallerClassName; -import static io.microsphere.reflect.ReflectionUtils.getCallerClassNameInGeneralJVM; -import static io.microsphere.reflect.ReflectionUtils.getCallerClassNameInSunJVM; +import static io.microsphere.reflect.ReflectionUtils.getCallerClassNameInStackTrace; +import static io.microsphere.reflect.ReflectionUtils.getCallerClassNameInSunReflectReflection; +import static io.microsphere.reflect.ReflectionUtils.getCallerClassNamesInStackWalker; import static io.microsphere.reflect.ReflectionUtils.isInaccessibleObjectException; import static io.microsphere.reflect.ReflectionUtils.isSupportedSunReflectReflection; import static io.microsphere.reflect.ReflectionUtils.readFieldsAsMap; import static io.microsphere.reflect.ReflectionUtils.toList; import static io.microsphere.reflect.ReflectionUtils.toObject; import static io.microsphere.util.ArrayUtils.ofArray; +import static io.microsphere.util.VersionUtils.JAVA_VERSION_9; +import static io.microsphere.util.VersionUtils.testCurrentJavaVersion; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -37,39 +41,71 @@ */ class ReflectionUtilsTest { + private static final Class CALLER_CLASS = ReflectionUtilsTest.class; + + private static final String CALLER_CLASS_NAME = CALLER_CLASS.getName(); + @Test void testGetCallerClassX() { - Class expectedClass = ReflectionUtilsTest.class; - Class callerClass = getCallerClass(); - assertEquals(expectedClass, callerClass); + assertEquals(CALLER_CLASS, callerClass); + Class callerClassInSunReflectReflection = getCallerClassInSunReflectReflection(); if (isSupportedSunReflectReflection()) { - Class callerClassInSunJVM = getCallerClassInSunJVM(); - assertEquals(callerClassInSunJVM, callerClass); + assertEquals(callerClassInSunReflectReflection, callerClass); + } else { + assertNull(callerClassInSunReflectReflection); } - Class callerClassInGeneralJVM = getCallerClassInGeneralJVM(); - assertEquals(callerClassInGeneralJVM, callerClass); + Class callerClassInStatckTrace = getCallerClassInStatckTrace(); + assertEquals(callerClassInStatckTrace, callerClass); + } + @Test + void testGetCallerClassWithFrame() { + assertNull(getCallerClass(99999)); } @Test void testGetCallerClassName() { - String expectedClassName = ReflectionUtilsTest.class.getName(); - String callerClassName = getCallerClassName(); - assertEquals(expectedClassName, callerClassName); + assertEquals(CALLER_CLASS_NAME, callerClassName); if (isSupportedSunReflectReflection()) { - String callerClassNameInSunJVM = getCallerClassNameInSunJVM(); + String callerClassNameInSunJVM = getCallerClassNameInSunReflectReflection(); assertEquals(callerClassNameInSunJVM, callerClassName); } - String callerClassNameInGeneralJVM = getCallerClassNameInGeneralJVM(); + String callerClassNameInGeneralJVM = getCallerClassNameInStackTrace(); assertEquals(callerClassNameInGeneralJVM, callerClassName); } + @Test + void testGetCallerClassNameOnStackWalkerSupportedForTesting() { + assertEquals(getCallerClassNameInStackTrace(), getCallerClassName(null, 0)); + assertEquals(CALLER_CLASS_NAME, getCallerClassName()); + } + + @Test + void testGetCallerClassNamesInStackWalker() { + if (testCurrentJavaVersion("<", JAVA_VERSION_9)) { + assertThrows(NullPointerException.class, () -> getCallerClassNamesInStackWalker()); + } else { + assertTrue(getCallerClassNamesInStackWalker().contains(CALLER_CLASS_NAME)); + } + } + + @Test + void testGetCallerClassNameInStackTrace() { + String callerClassName = getCallerClassNameInStackTrace(); + assertEquals(CALLER_CLASS_NAME, callerClassName); + } + + @Test + void testGetCallerClassInStatckTraceWithFrame() { + assertNull(getCallerClassInStatckTrace(99999)); + } + @Test void testToList() { int[] intArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}; From b93bf241335faaf79c1959b3c8b49e9b08a60cd3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 11:58:51 +0800 Subject: [PATCH 361/438] Refactor ReflectionUtils benchmark methods Update ReflectionUtilsBenchmark: add static imports for new helpers, rename benchmark methods to reflect StackWalker/StackTrace strategies, and add a benchmark that invokes the sun.reflect.Reflection-based lookup (commented out). These changes clarify which caller lookup approach is being measured and prepare the test for additional comparisons. --- .../reflect/ReflectionUtilsBenchmark.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsBenchmark.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsBenchmark.java index fc2ec7771..a6d12aa7b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsBenchmark.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsBenchmark.java @@ -25,6 +25,8 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import static io.microsphere.reflect.ReflectionUtils.getCallerClassInSunReflectReflection; +import static io.microsphere.reflect.ReflectionUtils.getCallerClassNamesInStackWalker; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.openjdk.jmh.annotations.Mode.AverageTime; @@ -44,13 +46,23 @@ @State(Scope.Thread) public class ReflectionUtilsBenchmark { +// @Benchmark +// public void getCallerClassDirectly() { +// sun.reflect.Reflection.getCallerClass(1); +// } + + @Benchmark + public void getCallerClassOnMethodHanle() { + getCallerClassInSunReflectReflection(); + } + @Benchmark - public void getCallerClassNameInSunJVM() { - ReflectionUtils.getCallerClassNameInSunJVM(); + public void getCallerClassNameInStackWalker() { + getCallerClassNamesInStackWalker(); } @Benchmark - public void getCallerClassNameInGeneralJVM() { - ReflectionUtils.getCallerClassNameInGeneralJVM(); + public void getCallerClassNameOnStackTrace() { + ReflectionUtils.getCallerClassNameInStackTrace(); } } From 550f41cb1ea4c417dafb7262d1142cdc8e932b7a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 12:06:26 +0800 Subject: [PATCH 362/438] Make getCallerClass robust to large frames Call getCallerClassName with a fixed index (1) instead of using invocationFrame + 1 to avoid out-of-range/throwing behavior for large frame values. Update the test to expect no exception when passing an excessively large frame (use assertDoesNotThrow) and add the necessary import. --- .../src/main/java/io/microsphere/reflect/ReflectionUtils.java | 2 +- .../test/java/io/microsphere/reflect/ReflectionUtilsTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java index 19b124b20..62f70c4ec 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java @@ -442,7 +442,7 @@ public static Class getCallerClass(int invocationFrame) { if (callerClass != null) { return callerClass; } - String className = getCallerClassName(stackWalkerInstance, invocationFrame + 1); + String className = getCallerClassName(stackWalkerInstance, 1); return resolveClass(className); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java index a82e07686..a5285203a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java @@ -26,6 +26,7 @@ import static io.microsphere.util.VersionUtils.JAVA_VERSION_9; import static io.microsphere.util.VersionUtils.testCurrentJavaVersion; import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -63,7 +64,7 @@ void testGetCallerClassX() { @Test void testGetCallerClassWithFrame() { - assertNull(getCallerClass(99999)); + assertDoesNotThrow(() -> getCallerClass(99999)); } @Test From fd6b0003f7f197c172f1b56ea9988d68d3053dcc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 19:58:16 +0800 Subject: [PATCH 363/438] Increase StackWalker depth and adjust caller offset Raise the StackWalker limit from 5 to 9 in getCallerClassNamesInStackWalker to collect more frames. Replace the previous hardcoded caller offset (1) with a computed offset (invocationFrame + 2) when resolving the caller class, ensuring correct caller resolution for deeper or nested invocation stacks. --- .../src/main/java/io/microsphere/reflect/ReflectionUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java index 62f70c4ec..bdf2c5158 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java @@ -297,7 +297,7 @@ static List getCallerClassNamesInStackWalker() { } private static List getCallerClassNamesInStackWalker(Stream stackFrames) { - return stackFrames.limit(5) + return stackFrames.limit(9) .map(ReflectionUtils::getClassName) .collect(Collectors.toList()); } @@ -442,7 +442,7 @@ public static Class getCallerClass(int invocationFrame) { if (callerClass != null) { return callerClass; } - String className = getCallerClassName(stackWalkerInstance, 1); + String className = getCallerClassName(stackWalkerInstance, invocationFrame + 2); return resolveClass(className); } From 2f7daf2b013f1707dd0b93f34dcc98b21fab5ac8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 20:00:59 +0800 Subject: [PATCH 364/438] Remove StackWalker logic from StackTraceUtils Remove StackWalker-based caller lookup and related reflection/version utilities from StackTraceUtils. This change deletes imports, constants, static initializers, logger, and methods such as getCallerClassName, getCallerClassNames, getCallerClassNameInGeneralJVM and other StackWalker/StackFrame helpers, leaving only getStackTrace() and the private constructor. The class is simplified to no longer perform stack-walking or reflectively invoke StackWalker APIs. --- .../io/microsphere/util/StackTraceUtils.java | 212 +----------------- 1 file changed, 1 insertion(+), 211 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java index f120b9d96..1f710a65c 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java @@ -17,22 +17,7 @@ package io.microsphere.util; import io.microsphere.annotation.Nonnull; -import io.microsphere.annotation.Nullable; -import io.microsphere.logging.Logger; -import java.lang.reflect.Method; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static io.microsphere.logging.LoggerFactory.getLogger; -import static io.microsphere.reflect.MethodUtils.findMethod; -import static io.microsphere.reflect.MethodUtils.invokeMethod; -import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; -import static io.microsphere.util.ClassLoaderUtils.resolveClass; -import static io.microsphere.util.VersionUtils.CURRENT_JAVA_VERSION; -import static io.microsphere.util.VersionUtils.JAVA_VERSION_9; import static java.lang.Thread.currentThread; /** @@ -44,124 +29,6 @@ */ public abstract class StackTraceUtils implements Utils { - private static final Class TYPE = StackTraceUtils.class; - - private static final Logger logger = getLogger(TYPE); - - private static final boolean IS_JDK_9_OR_LATER = CURRENT_JAVA_VERSION.ge(JAVA_VERSION_9); - - /** - * The class name of {@linkplain java.lang.StackWalker} that was introduced in JDK 9. - */ - public static final String STACK_WALKER_CLASS_NAME = "java.lang.StackWalker"; - - /** - * The class name of {@linkplain java.lang.StackWalker.Option} that was introduced in JDK 9. - */ - public static final String STACK_WALKER_OPTION_CLASS_NAME = "java.lang.StackWalker$Option"; - - /** - * The class name of {@linkplain java.lang.StackWalker.StackFrame} that was introduced in JDK 9. - */ - public static final String STACK_WALKER_STACK_FRAME_CLASS_NAME = "java.lang.StackWalker$StackFrame"; - - /** - * The {@link Class} of {@linkplain java.lang.StackWalker} that was introduced in JDK 9. - * (optional) - */ - public static final @Nullable Class STACK_WALKER_CLASS = resolveClass(STACK_WALKER_CLASS_NAME); - - /** - * The {@link Class} of {@linkplain java.lang.StackWalker.Option} that was introduced in JDK 9. - * (optional) - */ - public static final @Nullable Class STACK_WALKER_OPTION_CLASS = resolveClass(STACK_WALKER_OPTION_CLASS_NAME); - - /** - * The {@link Class} of {@linkplain java.lang.StackWalker.StackFrame} that was introduced in JDK 9. - * (optional) - */ - public static final @Nullable Class STACK_WALKER_STACK_FRAME_CLASS = resolveClass(STACK_WALKER_STACK_FRAME_CLASS_NAME); - - /** - * The name of {@linkplain java.lang.StackWalker.Option#RETAIN_CLASS_REFERENCE} - */ - static final String RETAIN_CLASS_REFERENCE_OPTION_NAME = "RETAIN_CLASS_REFERENCE"; - - /** - * The name of {@linkplain java.lang.StackWalker.Option#SHOW_REFLECT_FRAMES} - */ - static final String SHOW_REFLECT_FRAMES_OPTION_NAME = "SHOW_REFLECT_FRAMES"; - - /** - * The name of {@linkplain java.lang.StackWalker.Option#SHOW_HIDDEN_FRAMES} - */ - static final String SHOW_HIDDEN_FRAMES_OPTION_NAME = "SHOW_HIDDEN_FRAMES"; - - /** - * The {@link Method method} name of {@linkplain java.lang.StackWalker#getInstance()} - */ - static final String GET_INSTANCE_METHOD_NAME = "getInstance"; - - /** - * The {@link Method method} name of {{@linkplain java.lang.StackWalker#walk(java.util.function.Function)} - */ - static final String WALK_METHOD_NAME = "walk"; - - /** - * The {@link Method method} name of {@linkplain java.lang.StackWalker.StackFrame#getClassName()} - */ - static final String GET_CLASS_NAME_METHOD_NAME = "getClassName"; - - static final Method WALK_METHOD = findMethod(STACK_WALKER_CLASS, WALK_METHOD_NAME, Function.class); - - static final Method GET_CLASS_NAME_METHOD = findMethod(STACK_WALKER_STACK_FRAME_CLASS, GET_CLASS_NAME_METHOD_NAME); - - private static @Nullable Object stackWalkerInstance; - - private static final Function, Object> getClassNamesFunction = StackTraceUtils::getCallerClassNames; - - /** - * {@link StackTraceElement} invocation frame. - */ - private static final int stackTraceElementInvocationFrame; - - /** - * {@linkplain java.lang.StackWalker} invocation frame. - */ - private static final int stackWalkerInvocationFrame; - - // Initialize java.lang.StackWalker - static { - int invocationFrame = 0; - if (IS_JDK_9_OR_LATER) { - stackWalkerInstance = invokeStaticMethod(STACK_WALKER_CLASS, GET_INSTANCE_METHOD_NAME); - List stackFrameClassNames = getCallerClassNames(); - for (String stackFrameClassName : stackFrameClassNames) { - if (TYPE.getName().equals(stackFrameClassName)) { - break; - } - invocationFrame++; - } - } - stackWalkerInvocationFrame = invocationFrame + 2; - } - - // Initialize java.lang.StackTraceElement - static { - int invocationFrame = 0; - // Use java.lang.StackTraceElement to calculate frame - StackTraceElement[] stackTraceElements = getStackTrace(); - for (StackTraceElement stackTraceElement : stackTraceElements) { - String className = stackTraceElement.getClassName(); - if (TYPE.getName().equals(className)) { - break; - } - invocationFrame++; - } - stackTraceElementInvocationFrame = invocationFrame; - } - /** * Get the {@link StackTraceElement} array on the current thread * @@ -188,83 +55,6 @@ public static StackTraceElement[] getStackTrace() { return currentThread().getStackTrace(); } - /** - * Retrieves the fully qualified name of the class that called this method. - * - *

    This method utilizes either {@link java.lang.StackWalker} (available in JDK 9+) or falls back to - * using {@link StackTraceElement} to determine the caller's class name. It ensures compatibility across different JVM versions.

    - * - *

    Example Usage

    - *
    {@code
    -     * String callerClassName = StackTraceUtils.getCallerClassName();
    -     * System.out.println("Caller class: " + callerClassName);
    -     * }
    - * - * @return the fully qualified name of the calling class - * @throws IndexOutOfBoundsException if the stack trace does not have enough frames to determine the caller - */ - public static String getCallerClassName() { - return getCallerClassName(stackWalkerInstance, 1); - } - - static String getCallerClassName(Object stackWalkerInstance, int frameOffSet) { - if (stackWalkerInstance == null) { - // Plugs 1 , because Invocation getStackTrace() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClassName() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClassNameInGeneralJVM(int) method was considered as increment invocation frame - return getCallerClassNameInGeneralJVM(stackTraceElementInvocationFrame + 3 + frameOffSet); - } - List callerClassNames = getCallerClassNames(stackWalkerInstance); - String className = callerClassNames.get(stackWalkerInvocationFrame + frameOffSet); - return className; - } - - static List getCallerClassNames(Object stackWalkerInstance) { - return invokeMethod(stackWalkerInstance, WALK_METHOD, getClassNamesFunction); - } - - static List getCallerClassNames() { - return invokeMethod(stackWalkerInstance, WALK_METHOD, getClassNamesFunction); - } - - private static List getCallerClassNames(Stream stackFrames) { - return stackFrames.limit(5) - .map(StackTraceUtils::getClassName) - .collect(Collectors.toList()); - } - - private static String getClassName(Object stackFrame) { - return invokeMethod(stackFrame, GET_CLASS_NAME_METHOD); - } - - /** - * General implementation, get the calling class name - * - * @return call class name - * @see #getCallerClassNameInGeneralJVM(int) - */ - static String getCallerClassNameInGeneralJVM() { - // Plugs 1 , because Invocation getStackTrace() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClassNameInGeneralJVM() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClassNameInGeneralJVM(int) method was considered as increment invocation frame - return getCallerClassNameInGeneralJVM(stackTraceElementInvocationFrame + 3); - } - - /** - * General implementation, get the calling class name by specifying the calling level value - * - * @param invocationFrame invocation frame - * @return specified invocation frame class - */ - static String getCallerClassNameInGeneralJVM(int invocationFrame) throws IndexOutOfBoundsException { - StackTraceElement[] elements = getStackTrace(); - if (invocationFrame < elements.length) { - StackTraceElement targetStackTraceElement = elements[invocationFrame]; - return targetStackTraceElement.getClassName(); - } - return null; - } - private StackTraceUtils() { } -} +} \ No newline at end of file From afceab24fce68432bf3ddcd435b369354ca67bb4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 20:01:13 +0800 Subject: [PATCH 365/438] Reduce StackTraceUtilsTest to test getStackTrace Replace multiple caller-name and Java-version-dependent tests with a single simple test that asserts StackTraceUtils.getStackTrace() is not null. Clean up imports accordingly and remove tests for getCallerClassName, getCallerClassNames and VersionUtils checks. --- .../microsphere/util/StackTraceUtilsTest.java | 45 ++----------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java index d774da899..ac1844f59 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java @@ -2,16 +2,8 @@ import org.junit.jupiter.api.Test; -import static io.microsphere.util.StackTraceUtils.getCallerClassName; -import static io.microsphere.util.StackTraceUtils.getCallerClassNameInGeneralJVM; -import static io.microsphere.util.StackTraceUtils.getCallerClassNames; -import static io.microsphere.util.VersionUtils.JAVA_VERSION_9; -import static io.microsphere.util.VersionUtils.testCurrentJavaVersion; -import static java.lang.Integer.MAX_VALUE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static io.microsphere.util.StackTraceUtils.getStackTrace; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * {@link StackTraceUtils} Test @@ -22,37 +14,8 @@ */ class StackTraceUtilsTest { - private static final String CALLER_CLASS_NAME = StackTraceUtilsTest.class.getName(); - - @Test - void testGetCallerClassName() { - assertEquals(CALLER_CLASS_NAME, getCallerClassName()); - } - - @Test - void testGetCallerClassNameOnStackWalkerSupportedForTesting() { - assertEquals(getCallerClassNameInGeneralJVM(), getCallerClassName(null, 0)); - assertEquals(CALLER_CLASS_NAME, getCallerClassName()); - } - @Test - void testGetCallerClassNames() { - if (testCurrentJavaVersion("<", JAVA_VERSION_9)) { - assertThrows(NullPointerException.class, () -> getCallerClassNames()); - } else { - assertTrue(getCallerClassNames().contains(CALLER_CLASS_NAME)); - } + void testGetStackTrace() { + assertNotNull(getStackTrace()); } - - @Test - void testGetCallerClassNameInGeneralJVM() { - String callerClassName = getCallerClassNameInGeneralJVM(); - assertEquals(CALLER_CLASS_NAME, callerClassName); - } - - @Test - void testGetCallerClassNameInGeneralJVMOnOverStack() { - assertNull(getCallerClassNameInGeneralJVM(MAX_VALUE)); - } - } \ No newline at end of file From 4cc004ab0aab54a26dd6a1cdefc5839d7ec1c382 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 20:08:24 +0800 Subject: [PATCH 366/438] Remove StackTraceElementUtilsBenchmark test Delete the JMH benchmark class StackTraceElementUtilsBenchmark from microsphere-java-core tests. Removes io/microsphere/util/StackTraceElementUtilsBenchmark.java which contained benchmarks for StackTraceUtils (getCallerClassName and getCallerClassNameInGeneralJVM). --- .../util/StackTraceElementUtilsBenchmark.java | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 microsphere-java-core/src/test/java/io/microsphere/util/StackTraceElementUtilsBenchmark.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceElementUtilsBenchmark.java b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceElementUtilsBenchmark.java deleted file mode 100644 index 306ec684c..000000000 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceElementUtilsBenchmark.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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. - */ -package io.microsphere.util; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -import static io.microsphere.util.StackTraceUtils.getCallerClassName; -import static io.microsphere.util.StackTraceUtils.getCallerClassNameInGeneralJVM; -import static java.util.concurrent.TimeUnit.NANOSECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.openjdk.jmh.annotations.Mode.AverageTime; - -/** - * {@link StackTraceUtils} Benchmark - * - * @author
    Mercy - * @see StackTraceUtils - * @since 1.0.0 - */ -@Warmup(iterations = 5, time = 1, timeUnit = SECONDS) -@Measurement(iterations = 20, time = 1, timeUnit = SECONDS) -@Fork(1) -@BenchmarkMode(AverageTime) -@OutputTimeUnit(NANOSECONDS) -@State(Scope.Thread) -public class StackTraceElementUtilsBenchmark { - - @Benchmark - public void testGetCallerClassNameInGeneralJVM() { - getCallerClassNameInGeneralJVM(); - } - - @Benchmark - public void testGetCallerClassName() { - getCallerClassName(); - } -} From 54bb2ae8072f4d34160dc5510ef7f7f4ecc3527a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 21:28:00 +0800 Subject: [PATCH 367/438] Add stack-trace based caller lookup utilities Introduce utilities to determine caller classes/names using Thread#getStackTrace. A static invocationFrameOffset is computed at class init by scanning StackTraceElement frames for this utility class, and helper methods were added: getCallerClassInStatckTrace(), getCallerClassNameInStackTrace(), getCallerClassInStatckTrace(int) and getCallerClassNameInStackTrace(int). The implementation resolves class names to Class via resolveClass and returns null when a target frame is unavailable. --- .../io/microsphere/util/StackTraceUtils.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java index 1f710a65c..4ad4b6f97 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/StackTraceUtils.java @@ -18,6 +18,7 @@ import io.microsphere.annotation.Nonnull; +import static io.microsphere.util.ClassLoaderUtils.resolveClass; import static java.lang.Thread.currentThread; /** @@ -29,6 +30,82 @@ */ public abstract class StackTraceUtils implements Utils { + private static final Class TYPE = StackTraceUtils.class; + + /** + * {@link StackTraceElement} invocation frame offset + */ + private static final int invocationFrameOffset; + + // Initialize java.lang.StackTraceElement + static { + int offset = 0; + // Use java.lang.StackTraceElement to calculate frame + StackTraceElement[] stackTraceElements = getStackTrace(); + for (; ; offset++) { + StackTraceElement stackTraceElement = stackTraceElements[offset]; + String className = stackTraceElement.getClassName(); + if (TYPE.getName().equals(className)) { + break; + } + } + invocationFrameOffset = offset; + } + + /** + * Get caller class from {@link Thread#getStackTrace() stack traces} + * + * @return Caller Class + * @see #getCallerClassInStatckTrace(int) + */ + public static Class getCallerClassInStatckTrace() { + // Plus 1 , because Invocation getStackTrace() method was considered as increment invocation frame + // Plus 1 , because Invocation getCallerClassNameInStackTrace(int) method was considered as increment invocation frame + // Plus 1 , because Invocation getCallerClassInStatckTrace(int) method was considered as increment invocation frame + return getCallerClassInStatckTrace(invocationFrameOffset + 3); + } + + /** + * General implementation, get the calling class name + * + * @return call class name + * @see #getCallerClassNameInStackTrace(int) + */ + public static String getCallerClassNameInStackTrace() { + // Plus 1 , because Invocation getStackTrace() method was considered as increment invocation frame + // Plus 1 , because Invocation getCallerClassNameInStackTrace() method was considered as increment invocation frame + // Plus 1 , because Invocation getCallerClassNameInStackTrace(int) method was considered as increment invocation frame + return getCallerClassNameInStackTrace(invocationFrameOffset + 3); + } + + /** + * Get caller class in General JVM + * + * @param invocationFrame invocation frame + * @return caller class + * @see #getCallerClassNameInStackTrace(int) + */ + public static Class getCallerClassInStatckTrace(int invocationFrame) { + // Plus 1 , because Invocation getCallerClassNameInStackTrace(int) method was considered as increment invocation frame + String className = getCallerClassNameInStackTrace(invocationFrame + 1); + return className == null ? null : resolveClass(className); + } + + /** + * General implementation, get the calling class name by specifying the calling level value + * + * @param invocationFrame invocation frame + * @return specified invocation frame class + */ + public static String getCallerClassNameInStackTrace(int invocationFrame) throws IndexOutOfBoundsException { + StackTraceElement[] elements = getStackTrace(); + if (invocationFrame < elements.length) { + StackTraceElement targetStackTraceElement = elements[invocationFrame]; + return targetStackTraceElement.getClassName(); + } + return null; + } + /** * Get the {@link StackTraceElement} array on the current thread * From 46fcf6ac28adbd4559d67a7d5c17b70698d02b42 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 1 Mar 2026 21:28:13 +0800 Subject: [PATCH 368/438] Add tests for StackTraceUtils caller methods Add unit tests to StackTraceUtilsTest covering caller-related utilities. Introduces CALLER_CLASS and CALLER_CLASS_NAME constants and tests for getCallerClassInStatckTrace(), getCallerClassNameInStackTrace(), and an out-of-range frame case (assertNull). Also adds necessary static imports for the new assertions and methods. --- .../microsphere/util/StackTraceUtilsTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java index ac1844f59..12fb90663 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StackTraceUtilsTest.java @@ -2,8 +2,12 @@ import org.junit.jupiter.api.Test; +import static io.microsphere.util.StackTraceUtils.getCallerClassInStatckTrace; +import static io.microsphere.util.StackTraceUtils.getCallerClassNameInStackTrace; import static io.microsphere.util.StackTraceUtils.getStackTrace; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; /** * {@link StackTraceUtils} Test @@ -14,6 +18,27 @@ */ class StackTraceUtilsTest { + private static final Class CALLER_CLASS = StackTraceUtilsTest.class; + + private static final String CALLER_CLASS_NAME = CALLER_CLASS.getName(); + + @Test + void testGetCallerClassInStatckTrace() { + Class callerClassInStatckTrace = getCallerClassInStatckTrace(); + assertEquals(CALLER_CLASS, callerClassInStatckTrace); + } + + @Test + void testGetCallerClassNameInStackTrace() { + String callerClassName = getCallerClassNameInStackTrace(); + assertEquals(CALLER_CLASS_NAME, callerClassName); + } + + @Test + void testGetCallerClassInStatckTraceWithFrame() { + assertNull(getCallerClassInStatckTrace(99999)); + } + @Test void testGetStackTrace() { assertNotNull(getStackTrace()); From 55b88a7af5dbf686585b0853a764c486f5651be1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 2 Mar 2026 10:11:44 +0800 Subject: [PATCH 369/438] Refactor caller-class stack frame offsets Rename and consolidate stack-frame offset fields and simplify caller-class detection logic. Fields like sunReflectReflectionInvocationFrame and stackWalkerInvocationFrame were renamed to sunReflectReflectionInvocationFrameOffset and stackWalkerInvocationFrameOffset, and offset calculations were adjusted to account for invocation-level increments. Removed several stack-trace-based helper methods and fallbacks, simplified getCallerClassInSunReflectReflection to always try the MethodHandle call, and updated StackWalker-related logic to use the new offsets. Tests were updated to import StackTraceUtils helpers and to reflect the new offset behavior and assertions. This change reduces reliance on fragile stack-trace indexing and unifies offset handling across JVM detection paths. --- .../microsphere/reflect/ReflectionUtils.java | 163 ++++-------------- .../reflect/ReflectionUtilsTest.java | 25 +-- 2 files changed, 33 insertions(+), 155 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java index bdf2c5158..2f5e5aaf9 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/ReflectionUtils.java @@ -32,8 +32,7 @@ import static io.microsphere.util.ClassUtils.getType; import static io.microsphere.util.ClassUtils.isPrimitive; import static io.microsphere.util.ClassUtils.isSimpleType; -import static io.microsphere.util.StackTraceUtils.getStackTrace; -import static java.lang.Thread.currentThread; +import static io.microsphere.util.StackTraceUtils.getCallerClassNameInStackTrace; import static java.lang.reflect.Array.get; import static java.lang.reflect.Array.getLength; import static java.util.Collections.emptyMap; @@ -85,14 +84,9 @@ public abstract class ReflectionUtils implements Utils { private static final MethodHandle getCallerClassMethodHandle = findPublicStatic(SUN_REFLECT_REFLECTION_CLASS, getCallerClassMethodName, int.class); /** - * sun.reflect.Reflection invocation frame + * sun.reflect.Reflection invocation frame offset */ - private static final int sunReflectReflectionInvocationFrame; - - /** - * {@link StackTraceElement} invocation frame - */ - private static final int stackTraceElementInvocationFrame; + private static final int sunReflectReflectionInvocationFrameOffset; /** * Is Supported sun.reflect.Reflection ? @@ -146,9 +140,9 @@ public abstract class ReflectionUtils implements Utils { private static Object stackWalkerInstance; /** - * {@linkplain java.lang.StackWalker} invocation frame. + * {@linkplain java.lang.StackWalker} invocation frame offset. */ - private static final int stackWalkerInvocationFrame; + private static final int stackWalkerInvocationFrameOffset; private static final Function, Object> getClassNamesFunction = ReflectionUtils::getCallerClassNamesInStackWalker; @@ -177,7 +171,7 @@ public abstract class ReflectionUtils implements Utils { invocationFrame++; } } - stackWalkerInvocationFrame = invocationFrame + 2; + stackWalkerInvocationFrameOffset = invocationFrame; } // Initialize sun.reflect.Reflection @@ -194,26 +188,9 @@ public abstract class ReflectionUtils implements Utils { } } - // set method info - // getCallerClass() -> getCallerClass(int) - // Plugs 1 , because Invocation getCallerClass() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClass(int) method was considered as increment invocation frame - sunReflectReflectionInvocationFrame = invocationFrame + 2; - } - - // Initialize java.lang.StackTraceElement - static { - int invocationFrame = 0; - // Use java.lang.StackTraceElement to calculate frame - StackTraceElement[] stackTraceElements = currentThread().getStackTrace(); - for (StackTraceElement stackTraceElement : stackTraceElements) { - String className = stackTraceElement.getClassName(); - if (TYPE.getName().equals(className)) { - break; - } - invocationFrame++; - } - stackTraceElementInvocationFrame = invocationFrame; + // Plus 1 , because Invocation getCallerClass()/getCallerClassName() method was considered as increment invocation frame + // Plus 1 , because Invocation getCallerClassInSunReflectReflection(int) method was considered as increment invocation frame + sunReflectReflectionInvocationFrameOffset = invocationFrame + 2; } /** @@ -267,20 +244,25 @@ public static boolean isSupportedSunReflectReflection() { */ @Nonnull public static String getCallerClassName() { - Class callerClass = getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrame); - return callerClass == null ? getCallerClassName(stackWalkerInstance, 1) : callerClass.getName(); + if (supportedSunReflectReflection) { + return getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrameOffset).getName(); + } + return getCallerClassName(stackWalkerInstance, 1); } @Nullable static String getCallerClassName(Object stackWalkerInstance, int frameOffSet) { if (stackWalkerInstance == null) { - // Plugs 1 , because Invocation getStackTrace() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClassName() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClassNameInStackTrace(int) method was considered as increment invocation frame - return getCallerClassNameInStackTrace(stackTraceElementInvocationFrame + 3 + frameOffSet); + // Plus 1 , because Invocation getCallerClassName() method was considered as increment invocation frame + // Plus 1 , because Invocation getCallerClassName(Object stackWalkerInstance, int frameOffSet) method was considered as increment invocation frame + // Plus 1 , because Invocation getCallerClassNameInStackTrace(int) method was considered as increment invocation frame + return getCallerClassNameInStackTrace(3 + frameOffSet); } + + // Plus 1 , because Invocation getCallerClassName() method was considered as increment invocation frame + // Plus 1, because Invocation getCallerClassName(Object,int) method was considered as increment invocation frame List callerClassNames = getCallerClassNamesInStackWalker(stackWalkerInstance); - int frame = stackWalkerInvocationFrame + frameOffSet; + int frame = stackWalkerInvocationFrameOffset + 2 + frameOffSet; if (frame < callerClassNames.size()) { return callerClassNames.get(frame); } @@ -306,34 +288,6 @@ private static String getClassName(Object stackFrame) { return invokeMethod(stackFrame, GET_CLASS_NAME_METHOD); } - /** - * General implementation, get the calling class name - * - * @return call class name - * @see #getCallerClassNameInStackTrace(int) - */ - static String getCallerClassNameInStackTrace() { - // Plugs 1 , because Invocation getStackTrace() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClassNameInStackTrace() method was considered as increment invocation frame - // Plugs 1 , because Invocation getCallerClassNameInStackTrace(int) method was considered as increment invocation frame - return getCallerClassNameInStackTrace(stackTraceElementInvocationFrame + 3); - } - - /** - * General implementation, get the calling class name by specifying the calling level value - * - * @param invocationFrame invocation frame - * @return specified invocation frame class - */ - static String getCallerClassNameInStackTrace(int invocationFrame) throws IndexOutOfBoundsException { - StackTraceElement[] elements = getStackTrace(); - if (invocationFrame < elements.length) { - StackTraceElement targetStackTraceElement = elements[invocationFrame]; - return targetStackTraceElement.getClassName(); - } - return null; - } - /** * Gets the {@link Class} of the method caller. * @@ -357,7 +311,7 @@ static String getCallerClassNameInStackTrace(int invocationFrame) throws IndexOu */ @Nonnull public static Class getCallerClass() throws IllegalStateException { - Class callerClass = getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrame); + Class callerClass = getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrameOffset); if (callerClass != null) { return callerClass; } @@ -367,27 +321,13 @@ public static Class getCallerClass() throws IllegalStateException { @Nullable static Class getCallerClassInSunReflectReflection(int realFramesToSkip) { - if (supportedSunReflectReflection) { - try { - return (Class) getCallerClassMethodHandle.invokeExact(realFramesToSkip); - } catch (Throwable ignored) { - } + try { + return (Class) getCallerClassMethodHandle.invokeExact(realFramesToSkip); + } catch (Throwable ignored) { } return null; } - /** - * Get caller class in General JVM - * - * @param invocationFrame invocation frame - * @return caller class - * @see #getCallerClassNameInStackTrace(int) - */ - static Class getCallerClassInStatckTrace(int invocationFrame) { - String className = getCallerClassNameInStackTrace(invocationFrame + 1); - return className == null ? null : resolveClass(className); - } - /** * Get caller class In SUN HotSpot JVM * @@ -395,67 +335,22 @@ static Class getCallerClassInStatckTrace(int invocationFrame) { * @see #getCallerClassInSunReflectReflection(int) */ @Nullable - static Class getCallerClassInSunReflectReflection() throws UnsupportedOperationException { - return getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrame); + static Class getCallerClassInSunReflectReflection() { + return getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrameOffset); } /** * Get caller class name In SUN HotSpot JVM * * @return Caller Class - * @throws UnsupportedOperationException If JRE is not a SUN HotSpot JVM * @see #getCallerClassInSunReflectReflection(int) */ @Nullable - static String getCallerClassNameInSunReflectReflection() throws UnsupportedOperationException { - Class callerClass = getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrame); + static String getCallerClassNameInSunReflectReflection() { + Class callerClass = getCallerClassInSunReflectReflection(sunReflectReflectionInvocationFrameOffset); return callerClass == null ? null : callerClass.getName(); } - /** - * Retrieves the class of the caller at the specified invocation frame. - * - *

    This method attempts to use the internal Sun JDK class - * {@code sun.reflect.Reflection} for high-performance caller class detection if - * available and supported. If not supported (e.g., non-Sun/HotSpot JVM), it falls back to using - * the {@link StackTraceElement} approach.

    - * - *

    Example Usage

    - *
    {@code
    -     * public class Example {
    -     *     public void exampleMethod() {
    -     *         Class callerClass = ReflectionUtils.getCallerClass(2);
    -     *         System.out.println("Caller class: " + callerClass.getName());
    -     *     }
    -     * }
    -     * }
    - * - * @param invocationFrame The depth in the call stack to retrieve the caller class from. - * A value of 0 typically represents the immediate caller, but this may vary - * depending on the JVM implementation and call context. - * @return The class of the caller at the specified invocation frame. - * @throws IllegalStateException if an error occurs while determining the caller class. - */ - @Nullable - public static Class getCallerClass(int invocationFrame) { - Class callerClass = getCallerClassInSunReflectReflection(invocationFrame + 1); - if (callerClass != null) { - return callerClass; - } - String className = getCallerClassName(stackWalkerInstance, invocationFrame + 2); - return resolveClass(className); - } - - /** - * Get caller class from {@link Thread#getStackTrace() stack traces} - * - * @return Caller Class - * @see #getCallerClassInStatckTrace(int) - */ - static Class getCallerClassInStatckTrace() { - return getCallerClassInStatckTrace(stackTraceElementInvocationFrame + 3); - } - /** * Converts an array object into a {@link List}. * diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java index a5285203a..8060c6a43 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsTest.java @@ -11,10 +11,8 @@ import static io.microsphere.reflect.ReflectionUtils.INACCESSIBLE_OBJECT_EXCEPTION_CLASS; import static io.microsphere.reflect.ReflectionUtils.INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME; import static io.microsphere.reflect.ReflectionUtils.getCallerClass; -import static io.microsphere.reflect.ReflectionUtils.getCallerClassInStatckTrace; import static io.microsphere.reflect.ReflectionUtils.getCallerClassInSunReflectReflection; import static io.microsphere.reflect.ReflectionUtils.getCallerClassName; -import static io.microsphere.reflect.ReflectionUtils.getCallerClassNameInStackTrace; import static io.microsphere.reflect.ReflectionUtils.getCallerClassNameInSunReflectReflection; import static io.microsphere.reflect.ReflectionUtils.getCallerClassNamesInStackWalker; import static io.microsphere.reflect.ReflectionUtils.isInaccessibleObjectException; @@ -23,10 +21,11 @@ import static io.microsphere.reflect.ReflectionUtils.toList; import static io.microsphere.reflect.ReflectionUtils.toObject; import static io.microsphere.util.ArrayUtils.ofArray; +import static io.microsphere.util.StackTraceUtils.getCallerClassInStatckTrace; +import static io.microsphere.util.StackTraceUtils.getCallerClassNameInStackTrace; import static io.microsphere.util.VersionUtils.JAVA_VERSION_9; import static io.microsphere.util.VersionUtils.testCurrentJavaVersion; import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -53,7 +52,7 @@ void testGetCallerClassX() { Class callerClassInSunReflectReflection = getCallerClassInSunReflectReflection(); if (isSupportedSunReflectReflection()) { - assertEquals(callerClassInSunReflectReflection, callerClass); + assertEquals(CALLER_CLASS, callerClassInSunReflectReflection); } else { assertNull(callerClassInSunReflectReflection); } @@ -62,11 +61,6 @@ void testGetCallerClassX() { assertEquals(callerClassInStatckTrace, callerClass); } - @Test - void testGetCallerClassWithFrame() { - assertDoesNotThrow(() -> getCallerClass(99999)); - } - @Test void testGetCallerClassName() { String callerClassName = getCallerClassName(); @@ -83,7 +77,7 @@ void testGetCallerClassName() { @Test void testGetCallerClassNameOnStackWalkerSupportedForTesting() { - assertEquals(getCallerClassNameInStackTrace(), getCallerClassName(null, 0)); + assertEquals(getCallerClassNameInStackTrace(), getCallerClassName(null, 1)); assertEquals(CALLER_CLASS_NAME, getCallerClassName()); } @@ -96,17 +90,6 @@ void testGetCallerClassNamesInStackWalker() { } } - @Test - void testGetCallerClassNameInStackTrace() { - String callerClassName = getCallerClassNameInStackTrace(); - assertEquals(CALLER_CLASS_NAME, callerClassName); - } - - @Test - void testGetCallerClassInStatckTraceWithFrame() { - assertNull(getCallerClassInStatckTrace(99999)); - } - @Test void testToList() { int[] intArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}; From 912807a7d4067b0d3f528c4e450f32ac86041b31 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 2 Mar 2026 10:11:57 +0800 Subject: [PATCH 370/438] Use StackTraceUtils static import in benchmark Replace the fully-qualified ReflectionUtils.getCallerClassNameInStackTrace() call with a static import of getCallerClassNameInStackTrace from StackTraceUtils in ReflectionUtilsBenchmark. This clarifies the method origin and keeps the test import style consistent with other helper methods. --- .../java/io/microsphere/reflect/ReflectionUtilsBenchmark.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsBenchmark.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsBenchmark.java index a6d12aa7b..bc861f313 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsBenchmark.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectionUtilsBenchmark.java @@ -27,6 +27,7 @@ import static io.microsphere.reflect.ReflectionUtils.getCallerClassInSunReflectReflection; import static io.microsphere.reflect.ReflectionUtils.getCallerClassNamesInStackWalker; +import static io.microsphere.util.StackTraceUtils.getCallerClassNameInStackTrace; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.openjdk.jmh.annotations.Mode.AverageTime; @@ -63,6 +64,6 @@ public void getCallerClassNameInStackWalker() { @Benchmark public void getCallerClassNameOnStackTrace() { - ReflectionUtils.getCallerClassNameInStackTrace(); + getCallerClassNameInStackTrace(); } } From 8a285d07592e69da4da3986d8a5e47449444fb25 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 2 Mar 2026 10:12:16 +0800 Subject: [PATCH 371/438] Use stack-trace caller lookup and increment frame Replace reflection-based getCallerClass(...) with StackTraceUtils.getCallerClassInStatckTrace(...) and update import accordingly. Adjust the default caller frame in getCallerClassLoader() from 4 to 5 to account for the different stack depth used by the new stack-trace-based lookup. --- .../src/main/java/io/microsphere/util/ClassLoaderUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java index 4bb15f331..bcae9cacc 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java @@ -47,12 +47,12 @@ import static io.microsphere.reflect.FieldUtils.getFieldValue; import static io.microsphere.reflect.MethodUtils.findMethod; import static io.microsphere.reflect.MethodUtils.invokeMethod; -import static io.microsphere.reflect.ReflectionUtils.getCallerClass; import static io.microsphere.util.ArrayUtils.asArray; import static io.microsphere.util.Assert.assertNoNullElements; import static io.microsphere.util.Assert.assertNotNull; import static io.microsphere.util.ClassLoaderUtils.ResourceType.values; import static io.microsphere.util.ClassUtils.resolvePrimitiveClassForName; +import static io.microsphere.util.StackTraceUtils.getCallerClassInStatckTrace; import static io.microsphere.util.StringUtils.contains; import static io.microsphere.util.StringUtils.endsWith; import static io.microsphere.util.StringUtils.isBlank; @@ -288,7 +288,7 @@ public static ClassLoader getClassLoader(@Nullable Class loadedClass) { */ @Nullable public static ClassLoader getCallerClassLoader() { - return getCallerClassLoader(4); + return getCallerClassLoader(5); } /** @@ -1757,7 +1757,7 @@ public static URLClassLoader resolveURLClassLoader(@Nullable ClassLoader classLo */ static ClassLoader getCallerClassLoader(int invocationFrame) { ClassLoader classLoader = null; - Class callerClass = getCallerClass(invocationFrame); + Class callerClass = getCallerClassInStatckTrace(invocationFrame); if (callerClass != null) { classLoader = callerClass.getClassLoader(); } From 570c446ebbe37ba701f500cdbb44d361249e0313 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 2 Mar 2026 10:43:42 +0800 Subject: [PATCH 372/438] Increase caller depth in ClassLoaderUtils Change the stack depth used to resolve the caller ClassLoader from 4 to 5 when loadedClass is null. This adjusts the caller lookup to account for an additional stack frame in the call chain so the correct ClassLoader is returned, preventing incorrect loader resolution in certain call contexts. --- .../src/main/java/io/microsphere/util/ClassLoaderUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java index bcae9cacc..a1293c6f4 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java @@ -257,7 +257,7 @@ public static ClassLoader getClassLoader(@Nullable Class loadedClass) { ClassLoader classLoader = null; try { if (loadedClass == null) { - classLoader = getCallerClassLoader(4); + classLoader = getCallerClassLoader(5); } else { classLoader = loadedClass.getClassLoader(); } From da25f26709514a9ecb5e61b915a4ae5032695889 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 2 Mar 2026 14:06:36 +0800 Subject: [PATCH 373/438] Add SecurityUtils and tests Introduce SecurityUtils utility to manage the java.security.policy system property (constant, getter, and setters accepting String and File). Adds annotations and a private constructor. Include JUnit 5 tests (SecurityUtilsTest) that verify the constant and set/get behavior, prepare a module base path for resources, and clear system properties after each test. --- .../microsphere/security/SecurityUtils.java | 77 +++++++++++++++++++ .../security/SecurityUtilsTest.java | 73 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/security/SecurityUtils.java create mode 100644 microsphere-java-core/src/test/java/io/microsphere/security/SecurityUtilsTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/security/SecurityUtils.java b/microsphere-java-core/src/main/java/io/microsphere/security/SecurityUtils.java new file mode 100644 index 000000000..ff80ca478 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/security/SecurityUtils.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package io.microsphere.security; + +import io.microsphere.annotation.ConfigurationProperty; +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.util.Utils; + +import java.io.File; + +import static io.microsphere.annotation.ConfigurationProperty.SYSTEM_PROPERTIES_SOURCE; +import static java.lang.System.getProperty; +import static java.lang.System.setProperty; + +/** + * The utilities class for Java Security + * + * @author
    Mercy + * @see SecurityManager + * @see SecurityException + * @since 1.0.0 + */ +public abstract class SecurityUtils implements Utils { + + /** + * The System Property name of Java Security Policy File. + */ + @ConfigurationProperty(source = SYSTEM_PROPERTIES_SOURCE) + public static final String JAVA_SECURITY_POLICY_FILE_PROPERTY_NAME = "java.security.policy"; + + /** + * Set the Java Security Policy File + * + * @param javaSecurityPolicyFilePath the absolute path of Java Security Policy File + */ + public static void setJavaSecurityPolicyFile(@Nonnull String javaSecurityPolicyFilePath) { + setProperty(JAVA_SECURITY_POLICY_FILE_PROPERTY_NAME, javaSecurityPolicyFilePath); + } + + /** + * Set the Java Security Policy File + * + * @param javaSecurityPolicyFile the Java Security Policy File + */ + public static void setJavaSecurityPolicyFile(@Nonnull File javaSecurityPolicyFile) { + setJavaSecurityPolicyFile(javaSecurityPolicyFile.getAbsolutePath()); + } + + /** + * Get the Java Security Policy File + * + * @return the Java Security Policy File + */ + @Nullable + public static String getJavaSecurityPolicyFile() { + return getProperty(JAVA_SECURITY_POLICY_FILE_PROPERTY_NAME); + } + + private SecurityUtils() { + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/security/SecurityUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/security/SecurityUtilsTest.java new file mode 100644 index 000000000..3ac8ddb6e --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/security/SecurityUtilsTest.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package io.microsphere.security; + + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.net.URL; + +import static io.microsphere.security.SecurityUtils.JAVA_SECURITY_POLICY_FILE_PROPERTY_NAME; +import static io.microsphere.security.SecurityUtils.getJavaSecurityPolicyFile; +import static io.microsphere.security.SecurityUtils.setJavaSecurityPolicyFile; +import static io.microsphere.util.ClassLoaderUtils.ResourceType.CLASS; +import static io.microsphere.util.ClassLoaderUtils.getResource; +import static io.microsphere.util.StringUtils.substringBefore; +import static java.lang.System.clearProperty; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link SecurityUtils} + * + * @author Mercy + * @see SecurityUtils + * @since 1.0.0 + */ +class SecurityUtilsTest { + + private static final String MODULE_BASE_PROPERTY_NAME = "module.base"; + + private static final URL policyResource = getResource("META-INF/class-load-test.policy"); + + private static final File policyFile = new File(policyResource.getFile()); + + static { + URL classResource = getResource(SecurityUtilsTest.class.getClassLoader(), CLASS, SecurityUtilsTest.class.getName() + ".class"); + String moduleBasePath = substringBefore(classResource.getFile(), "target/"); + System.setProperty(MODULE_BASE_PROPERTY_NAME, moduleBasePath); + } + + @AfterEach + void tearDown() { + clearProperty(JAVA_SECURITY_POLICY_FILE_PROPERTY_NAME); + clearProperty(MODULE_BASE_PROPERTY_NAME); + } + + @Test + void testtConstants() { + assertEquals("java.security.policy", JAVA_SECURITY_POLICY_FILE_PROPERTY_NAME); + } + + @Test + void testJavaSecurityPolicyFile() { + setJavaSecurityPolicyFile(policyFile); + assertEquals(policyFile.getAbsolutePath(), getJavaSecurityPolicyFile()); + } +} \ No newline at end of file From 3d2a2fa82bfba03974d9d8fcba9d42d0c2c7d083 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 2 Mar 2026 14:06:51 +0800 Subject: [PATCH 374/438] Add class-load test policy granting AllPermission Add class-load-test.policy under microsphere-java-core/src/test/resources/META-INF that grants java.security.AllPermission for test runs. This enables unrestricted class loading and security permissions during class-load related tests. --- .../src/test/resources/META-INF/class-load-test.policy | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 microsphere-java-core/src/test/resources/META-INF/class-load-test.policy diff --git a/microsphere-java-core/src/test/resources/META-INF/class-load-test.policy b/microsphere-java-core/src/test/resources/META-INF/class-load-test.policy new file mode 100644 index 000000000..4511f0aa5 --- /dev/null +++ b/microsphere-java-core/src/test/resources/META-INF/class-load-test.policy @@ -0,0 +1,4 @@ + +grant { + permission java.security.AllPermission; +}; \ No newline at end of file From f0f6cafa4e58667cdc1b72dda9a89a344788af4c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 10:58:51 +0800 Subject: [PATCH 375/438] Use default ClassLoader for getBundle Replace Thread.currentThread().getContextClassLoader() with ClassLoaderUtils.getDefaultClassLoader() in getBundle to standardize how the ClassLoader is obtained. Added static import for getDefaultClassLoader and removed the unused static import of Thread.currentThread(). This ensures a consistent/default class loader is used when loading resource bundles. --- .../java/io/microsphere/util/PropertyResourceBundleUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/PropertyResourceBundleUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/PropertyResourceBundleUtils.java index 2b88c777c..d9c5cef03 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/PropertyResourceBundleUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/PropertyResourceBundleUtils.java @@ -10,10 +10,10 @@ import java.util.PropertyResourceBundle; import java.util.ResourceBundle; +import static io.microsphere.util.ClassLoaderUtils.getDefaultClassLoader; import static io.microsphere.util.PropertyResourceBundleControl.newControl; import static io.microsphere.util.SystemUtils.FILE_ENCODING; import static java.lang.System.getProperty; -import static java.lang.Thread.currentThread; import static java.util.Locale.getDefault; /** @@ -141,7 +141,7 @@ public static ResourceBundle getBundle(String baseName, String encoding) { */ @Nonnull public static ResourceBundle getBundle(String baseName, Locale locale, String encoding) { - ClassLoader classLoader = currentThread().getContextClassLoader(); + ClassLoader classLoader = getDefaultClassLoader(); return getBundle(baseName, locale, classLoader, encoding); } From b3dd6d5c0c932b80fb1b4795f684596e946d703f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 13:13:07 +0800 Subject: [PATCH 376/438] Simplify ClassLoaderUtils null checks Refactor ClassLoaderUtils to simplify null handling and returns. Added static import of Objects.nonNull and replaced explicit null checks/ternary logic in getDefaultClassLoader and getClassLoader with more concise calls and returns. Use nonNull in isLoadedClass and delegate null-to-empty handling to ofSet for resource and class list returns, removing several redundant null checks and try/catch blocks for cleaner code while preserving fallback semantics (bootstrap -> system ClassLoader). --- .../io/microsphere/util/ClassLoaderUtils.java | 36 ++++++------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java index a1293c6f4..c029fb6f4 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java @@ -63,6 +63,7 @@ import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; +import static java.util.Objects.nonNull; /** @@ -208,24 +209,12 @@ public static void setVerbose(boolean value) { */ @Nullable public static ClassLoader getDefaultClassLoader() { - ClassLoader classLoader = null; - try { - classLoader = currentThread().getContextClassLoader(); - } catch (Throwable ignored) { - } - + ClassLoader classLoader = currentThread().getContextClassLoader(); if (classLoader == null) { classLoader = ClassLoaderUtils.class.getClassLoader(); } - - if (classLoader == null) { - // classLoader is null indicates the bootstrap ClassLoader - try { - classLoader = getSystemClassLoader(); - } catch (Throwable ignored) { - } - } - return classLoader; + // classLoader is null indicates the bootstrap ClassLoader + return classLoader == null ? getSystemClassLoader() : classLoader; } /** @@ -254,15 +243,10 @@ public static ClassLoader getDefaultClassLoader() { */ @Nullable public static ClassLoader getClassLoader(@Nullable Class loadedClass) { - ClassLoader classLoader = null; - try { - if (loadedClass == null) { - classLoader = getCallerClassLoader(5); - } else { - classLoader = loadedClass.getClassLoader(); - } - } catch (SecurityException ignored) { + if (loadedClass == null) { + return getCallerClassLoader(5); } + ClassLoader classLoader = loadedClass.getClassLoader(); return classLoader == null ? getDefaultClassLoader() : classLoader; } @@ -419,7 +403,7 @@ public static boolean isLoadedClass(@Nullable ClassLoader classLoader, Class * @return true if the class is already loaded; false otherwise */ public static boolean isLoadedClass(@Nullable ClassLoader classLoader, String className) { - return findLoadedClass(classLoader, className) != null; + return nonNull(findLoadedClass(classLoader, className)); } /** @@ -611,7 +595,7 @@ public static Set getResources(@Nullable ClassLoader classLoader, @Nonnull ClassLoader actualClassLoader = findClassLoader(classLoader); String normalizedResourceName = resourceType.resolve(resourceName); Enumeration resources = actualClassLoader.getResources(normalizedResourceName); - return resources != null && resources.hasMoreElements() ? ofSet(resources) : emptySet(); + return ofSet(resources); } /** @@ -1141,7 +1125,7 @@ public static Set> getLoadedClasses(@Nullable ClassLoader classLoader) ClassLoader actualClassLoader = findClassLoader(classLoader); Field field = findField(classLoaderClass, classesFieldName); List> classes = getFieldValue(actualClassLoader, field); - return classes == null ? emptySet() : ofSet(classes); + return ofSet(classes); } /** From d79b6ac7d168f1e25fc290fe6c89e51f3953b6f6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 13:13:21 +0800 Subject: [PATCH 377/438] Add resource-type and logging tests Switch the test to extend LoggingTest and implement Loggable, add a classLoader field and an @AfterEach reset to restore verbose state. Introduce new unit tests (caller-classloader out-of-bounds, getClassResourceWithClass, and ResourceType DEFAULT/CLASS/PACKAGE behavior). Add/import several test constants and ResourceType static imports and adjust imports accordingly. --- .../util/ClassLoaderUtilsTest.java | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsTest.java index f5ce33f1c..4165a1a0d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsTest.java @@ -3,8 +3,10 @@ */ package io.microsphere.util; -import io.microsphere.AbstractTestCase; +import io.microsphere.Loggable; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import javax.annotation.Nonnull; @@ -23,6 +25,11 @@ import java.util.Set; import java.util.TreeSet; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_COLLECTION; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_LIST; +import static io.microsphere.AbstractTestCase.TEST_EMPTY_SET; +import static io.microsphere.AbstractTestCase.TEST_NULL_ITERABLE; +import static io.microsphere.AbstractTestCase.TEST_NULL_STRING_ARRAY; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.ofEntry; import static io.microsphere.collection.MapUtils.toFixedMap; @@ -33,6 +40,9 @@ import static io.microsphere.util.ArrayUtils.EMPTY_URL_ARRAY; import static io.microsphere.util.ArrayUtils.asArray; import static io.microsphere.util.ArrayUtils.ofArray; +import static io.microsphere.util.ClassLoaderUtils.ResourceType.CLASS; +import static io.microsphere.util.ClassLoaderUtils.ResourceType.DEFAULT; +import static io.microsphere.util.ClassLoaderUtils.ResourceType.PACKAGE; import static io.microsphere.util.ClassLoaderUtils.doLoadClass; import static io.microsphere.util.ClassLoaderUtils.findAllClassPathURLs; import static io.microsphere.util.ClassLoaderUtils.findLoadedClass; @@ -88,7 +98,7 @@ * @see ClassLoaderUtils * @since 1.0.0 */ -class ClassLoaderUtilsTest extends AbstractTestCase { +class ClassLoaderUtilsTest extends LoggingTest implements Loggable { private static final boolean isLessThanJava12 = testCurrentJavaVersion("<", JAVA_VERSION_12); @@ -107,11 +117,18 @@ class ClassLoaderUtilsTest extends AbstractTestCase { "///META-INF//services/io.microsphere.event.EventListener" ); + private final ClassLoader classLoader = getClassLoader(getClass()); + @AfterAll static void afterAll() { setVerbose(verbose); } + @AfterEach + void reset() { + setVerbose(false); + } + @Test void testFields() throws Exception { @@ -211,6 +228,11 @@ void testGetCallerClassLoader() { assertSame(currentClass.getClassLoader(), getCallerClassLoader()); } + @Test + void testGetCallerClassLoaderOnFrameOutOfBound() { + assertNull(getCallerClassLoader(9999)); + } + @Test void testFindLoadedClasses() { Set> classes = findLoadedClasses(classLoader, currentClass.getName()); @@ -419,6 +441,11 @@ void testGetClassResource() { assertGetClassResource(this.classLoader); } + @Test + void testGetClassResourceWithClass() { + assertNotNull(getClassResource(ClassUtilsTest.class)); + } + @Test void testGetClassResourceOnNullClassLoader() { assertGetClassResource(null); @@ -653,6 +680,44 @@ void testLogOnFindLoadedClassInvocationFailed() { logOnFindLoadedClassInvocationFailed(null, null, null); } + @Test + void testDefaultResourceType() { + String name = "test"; + assertTrue(DEFAULT.supports(name)); + assertEquals(name, DEFAULT.normalize(name)); + + assertNull(DEFAULT.resolve(null)); + assertEquals(name, DEFAULT.resolve(name)); + } + + @Test + void testClassResourceType() { + String name = ClassLoaderUtilsTest.class.getName(); + assertFalse(CLASS.supports(null)); + assertFalse(CLASS.supports("")); + assertFalse(CLASS.supports(name)); + assertTrue(CLASS.supports("Test.class")); + + assertNull(CLASS.normalize(null)); + assertEquals("io/microsphere/util/ClassLoaderUtilsTest.class", CLASS.normalize(name)); + + assertNull(CLASS.resolve(null)); + assertNull(CLASS.resolve(name));} + + @Test + void testPackageResourceType() { + assertFalse(PACKAGE.supports(null)); + assertFalse(PACKAGE.supports("/")); + assertFalse(PACKAGE.supports("\\")); + assertFalse(PACKAGE.supports("Test.class")); + assertTrue(PACKAGE.supports("")); + assertTrue(PACKAGE.supports("com.acme")); + + assertNull(PACKAGE.normalize(null)); + assertEquals("io/microsphere/util/", PACKAGE.normalize("io.microsphere.util")); + assertEquals("io/microsphere/util/", PACKAGE.normalize("io.microsphere.util.")); + } + private void assertInvokeFindLoadedClassMethod(Class clazz) { assertSame(clazz, invokeFindLoadedClassMethod(this.classLoader, clazz.getName())); } From 4342e5b7c7c1a7398dae43bcb5d18685c3399c85 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 13:21:26 +0800 Subject: [PATCH 378/438] Add test for JSONStringer.toString() Add a unit test asserting jsonStringer.toString() returns null, and import assertNull. Remove an empty placeholder testBeforeValue() method. Minor EOF newline adjustment. --- .../java/io/microsphere/json/JSONStringerTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/json/JSONStringerTest.java b/microsphere-java-core/src/test/java/io/microsphere/json/JSONStringerTest.java index 9ba81ba23..8d87c868c 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/json/JSONStringerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/json/JSONStringerTest.java @@ -28,6 +28,7 @@ import static io.microsphere.json.JSONStringer.Scope.NULL; import static java.lang.Boolean.TRUE; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; /** @@ -206,13 +207,14 @@ void testBeforeKeyOnJSONException() throws JSONException { assertThrows(JSONException.class, jsonStringer::beforeKey); } - @Test - void testBeforeValue() throws JSONException { - } - @Test void testBeforeValueOnJSONException() throws JSONException { jsonStringer.open(EMPTY_OBJECT, "["); assertThrows(JSONException.class, jsonStringer::beforeValue); } + + @Test + void testToString() { + assertNull(jsonStringer.toString()); + } } \ No newline at end of file From 74b03656a5309cb2ac4ce027e95197baf1b7eeb3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 14:30:31 +0800 Subject: [PATCH 379/438] Add assertion for trailing char in JSONTokener Update testReadEscapeCharacter to include an extra 'c' in the input string and add an assertion that readEscapeCharacter() returns 'c'. Ensures the tokener correctly returns a non-escape character following escape sequences. --- .../src/test/java/io/microsphere/json/JSONTokenerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/json/JSONTokenerTest.java b/microsphere-java-core/src/test/java/io/microsphere/json/JSONTokenerTest.java index 03d954146..241e1e3d1 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/json/JSONTokenerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/json/JSONTokenerTest.java @@ -209,7 +209,7 @@ void testNextStringOnSyntaxError() { @Test void testReadEscapeCharacter() throws JSONException { - JSONTokener t = new JSONTokener("ntbnrf\'\"\\u0041"); + JSONTokener t = new JSONTokener("ntbnrf\'\"\\u0041c"); assertEquals('\n', t.readEscapeCharacter()); assertEquals('\t', t.readEscapeCharacter()); assertEquals('\b', t.readEscapeCharacter()); @@ -220,6 +220,7 @@ void testReadEscapeCharacter() throws JSONException { assertEquals('"', t.readEscapeCharacter()); assertEquals('\\', t.readEscapeCharacter()); assertEquals('A', t.readEscapeCharacter()); + assertEquals('c', t.readEscapeCharacter()); } @Test From 84aa7493b864e6820f36eeb19374732d84e3e682 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 18:58:13 +0800 Subject: [PATCH 380/438] Add missing assertions in MapUtilsTest Add two assertions to strengthen MapUtilsTest: verify nestedMap correctly returns the top-level value for a map with dotted keys (A, A.B, A.B.C) and verify extraProperties returns an empty map for a single-entry map (A=1). These checks cover edge cases for nested key resolution and extraProperties handling. --- .../src/test/java/io/microsphere/collection/MapUtilsTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/MapUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/MapUtilsTest.java index 2885e5bf2..f99422e6a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/MapUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/MapUtilsTest.java @@ -411,6 +411,9 @@ void testNestedMap() { assertEquals("2", fMap.get("2")); assertEquals("3", fMap.get("3")); + map = ofMap("A", 1, "A.B", 2, "A.B.C", 3); + nestedMap = nestedMap(map); + assertEquals("1", nestedMap.get("A")); } @Test @@ -418,6 +421,7 @@ void testExtraProperties() { assertEquals(emptyMap(), extraProperties(null)); assertEquals(emptyMap(), extraProperties(emptyMap())); assertEquals(emptyMap(), extraProperties(newHashMap())); + assertEquals(emptyMap(), extraProperties(ofMap("A", 1))); } static void assertOfMap(Map map) { From e9cd780be656033adabf89e0cc54142b8ae54f57 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 19:05:38 +0800 Subject: [PATCH 381/438] Add test for configure(null) handling Extend ConfigurerTest with a case that calls configure((Object) null). The new test ensures the Configurer API can accept a null target and still perform compare/apply calls without throwing, increasing test coverage for null handling. --- .../src/test/java/io/microsphere/util/ConfigurerTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ConfigurerTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ConfigurerTest.java index a0bbaf561..4844becba 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ConfigurerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ConfigurerTest.java @@ -77,6 +77,11 @@ void test() { .value(() -> 1) .apply(value -> { }); + + configure((Object) null) + .compare(1) + .apply(value -> { + }); } } From cc9ecfe1b1e6f312776d9e78fd30f922fcd51fb6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 20:02:37 +0800 Subject: [PATCH 382/438] Add Iterators.equals for Iterator comparison Introduce io.microsphere.collection.Iterators utility class with a static equals(Iterator, Iterator) method that compares two iterators element-by-element using Objects.equals and correctly handles nulls and differing lengths. Class includes a private constructor to prevent instantiation and implements the Utils marker interface. File is added with ASF license header and author metadata. --- .../io/microsphere/collection/Iterators.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 microsphere-java-core/src/main/java/io/microsphere/collection/Iterators.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/Iterators.java b/microsphere-java-core/src/main/java/io/microsphere/collection/Iterators.java new file mode 100644 index 000000000..11e25b860 --- /dev/null +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/Iterators.java @@ -0,0 +1,54 @@ +/* + * 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. + */ + +package io.microsphere.collection; + +import io.microsphere.util.Utils; + +import java.util.Iterator; +import java.util.Objects; + +/** + * The utilties class for {@link Iterator} + * + * @author Mercy + * @see Iterator + * @since 1.0.0 + */ +public abstract class Iterators implements Utils { + + + public static boolean equals(Iterator one, Iterator another) { + if (one == another) { + return true; + } + if (one == null || another == null) { + return false; + } + + while (one.hasNext() && another.hasNext()) { + if (!Objects.equals(one.next(), another.next())) { + return false; + } + } + return !one.hasNext() && !another.hasNext(); + } + + private Iterators() { + } + +} \ No newline at end of file From 8eb74a6be19884aeb8cd43c2d650abf0de244ef1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 20:02:53 +0800 Subject: [PATCH 383/438] Add IteratorsTest for Iterators.equals Add a new JUnit 5 test class IteratorsTest that verifies Iterators.equals behavior. Tests cover same iterator, equal iterators, different iterators, null handling, and same-size but different-order iterators. Uses CollectionUtils.emptyIterator and Lists.ofList; file includes ASF license header and author tag. --- .../microsphere/collection/IteratorsTest.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/collection/IteratorsTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/IteratorsTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/IteratorsTest.java new file mode 100644 index 000000000..2ed2ed945 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/IteratorsTest.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package io.microsphere.collection; + + +import org.junit.jupiter.api.Test; + +import java.util.Iterator; +import java.util.List; + +import static io.microsphere.collection.CollectionUtils.emptyIterator; +import static io.microsphere.collection.Lists.ofList; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link Iterators} Test + * + * @author Mercy + * @see Iterators + * @since 1.0.0 + */ +class IteratorsTest { + + @Test + void testEquals() { + List values = ofList("A", "B", "C"); + Iterator iterator = values.iterator(); + + // the same iterator + assertTrue(Iterators.equals(iterator, iterator)); + // equal iterators + assertTrue(Iterators.equals(values.iterator(), values.iterator())); + // different iterators + assertFalse(Iterators.equals(values.iterator(), emptyIterator())); + assertFalse(Iterators.equals(emptyIterator(), values.iterator())); + // nulling + assertFalse(Iterators.equals(null, values.iterator())); + assertFalse(Iterators.equals(values.iterator(), null)); + + // the same-sizz iterators + assertFalse(Iterators.equals(values.iterator(), ofList("B", "C", "A").iterator())); + } +} \ No newline at end of file From f471c83569dba1a2f5e90f31cf6d9938a08103f8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 20:08:57 +0800 Subject: [PATCH 384/438] Add Javadoc to Iterators.equals Add a Javadoc comment for the Iterators.equals(Iterator, Iterator) method, documenting the two iterator parameters and the boolean return value. This improves API documentation without changing runtime behavior. --- .../main/java/io/microsphere/collection/Iterators.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/Iterators.java b/microsphere-java-core/src/main/java/io/microsphere/collection/Iterators.java index 11e25b860..4e16e201a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/collection/Iterators.java +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/Iterators.java @@ -31,7 +31,13 @@ */ public abstract class Iterators implements Utils { - + /** + * Equals two {@link Iterator Iterators} + * + * @param one {@link Iterator} + * @param another {@link Iterator} + * @return true if equals + */ public static boolean equals(Iterator one, Iterator another) { if (one == another) { return true; From 44a90d56272b0fb8385173103ed4c1e3db033a4a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 20:19:38 +0800 Subject: [PATCH 385/438] Use Iterators.equals to compare iterators Replace manual while-loop element comparisons with a call to Iterators.equals(iterator, otherIterator). This simplifies the code, reduces boilerplate, and ensures correct handling of element and length comparisons between the two iterators. --- .../main/java/io/microsphere/collection/ReversedDeque.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/ReversedDeque.java b/microsphere-java-core/src/main/java/io/microsphere/collection/ReversedDeque.java index 84113b5e0..574608267 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/collection/ReversedDeque.java +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/ReversedDeque.java @@ -180,12 +180,7 @@ public boolean equals(Object o) { Iterator iterator = iterator(); Iterator otherIterator = ((Iterable) o).iterator(); - while (iterator.hasNext() && otherIterator.hasNext()) { - if (!Objects.equals(iterator.next(), otherIterator.next())) { - return false; - } - } - return true; + return Iterators.equals(iterator, otherIterator); } @Override From d44f5d391f5afd951c2eed3fa1edc5d777f55dde Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 20:19:47 +0800 Subject: [PATCH 386/438] Qualify instance references with this in tests Replace unqualified 'instance' references with 'this.instance' throughout MutableCollectionTest to improve clarity and avoid potential shadowing; updates assertions, iterator usage, toArray/toArray(T[]), add/remove operations, collection bulk ops, forEach, hashCode and toString checks accordingly. --- .../collection/MutableCollectionTest.java | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/MutableCollectionTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/MutableCollectionTest.java index 9625430ea..73aa6ca44 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/MutableCollectionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/MutableCollectionTest.java @@ -55,32 +55,32 @@ void setUp() { protected abstract C newInstance(); protected void init(C instance) { - instance.add("A"); - instance.add("B"); - instance.add("C"); + this.instance.add("A"); + this.instance.add("B"); + this.instance.add("C"); } @Test void testSize() { - assertEquals(3, instance.size()); + assertEquals(3, this.instance.size()); } @Test void testIsEmpty() { - assertFalse(instance.isEmpty()); + assertFalse(this.instance.isEmpty()); } @Test void testContains() { - assertTrue(instance.contains("A")); - assertTrue(instance.contains("B")); - assertTrue(instance.contains("C")); - assertFalse(instance.contains("D")); + assertTrue(this.instance.contains("A")); + assertTrue(this.instance.contains("B")); + assertTrue(this.instance.contains("C")); + assertFalse(this.instance.contains("D")); } @Test void testIterator() { - Iterator iterator = instance.iterator(); + Iterator iterator = this.instance.iterator(); assertTrue(iterator.hasNext()); assertEquals("A", iterator.next()); assertTrue(iterator.hasNext()); @@ -92,115 +92,115 @@ void testIterator() { @Test void testToArray() { - Object[] array = instance.toArray(); + Object[] array = this.instance.toArray(); assertEquals(3, array.length); assertArray("A", "B", "C"); } @Test void testToArrayWithArray() { - Object[] array = instance.toArray(EMPTY_OBJECT_ARRAY); + Object[] array = this.instance.toArray(EMPTY_OBJECT_ARRAY); assertEquals(3, array.length); assertArray("A", "B", "C"); } void assertArray(Object... array) { - assertArrayEquals(array, instance.toArray()); + assertArrayEquals(array, this.instance.toArray()); } @Test void testAdd() { testSize(); - assertTrue(instance.add("D")); - assertEquals(4, instance.size()); + assertTrue(this.instance.add("D")); + assertEquals(4, this.instance.size()); } @Test void testRemove() { - assertTrue(instance.remove("A")); - assertFalse(instance.remove("A")); - assertTrue(instance.remove("B")); - assertTrue(instance.remove("C")); - assertFalse(instance.remove("D")); - instance.add(null); - assertTrue(instance.remove(null)); - assertFalse(instance.remove(null)); + assertTrue(this.instance.remove("A")); + assertFalse(this.instance.remove("A")); + assertTrue(this.instance.remove("B")); + assertTrue(this.instance.remove("C")); + assertFalse(this.instance.remove("D")); + this.instance.add(null); + assertTrue(this.instance.remove(null)); + assertFalse(this.instance.remove(null)); } @Test void testContainsAll() { - assertTrue(instance.containsAll(asList("A"))); - assertTrue(instance.containsAll(asList("A", "B"))); - assertTrue(instance.containsAll(asList("A", "B", "C"))); - assertFalse(instance.containsAll(asList("A", "B", "C", "D"))); - assertTrue(instance.containsAll(emptyList())); + assertTrue(this.instance.containsAll(asList("A"))); + assertTrue(this.instance.containsAll(asList("A", "B"))); + assertTrue(this.instance.containsAll(asList("A", "B", "C"))); + assertFalse(this.instance.containsAll(asList("A", "B", "C", "D"))); + assertTrue(this.instance.containsAll(emptyList())); } @Test void testAddAll() { testSize(); - assertTrue(instance.addAll(asList("A"))); - assertEquals(4, instance.size()); - assertTrue(instance.addAll(asList("B", "C"))); - assertEquals(6, instance.size()); + assertTrue(this.instance.addAll(asList("A"))); + assertEquals(4, this.instance.size()); + assertTrue(this.instance.addAll(asList("B", "C"))); + assertEquals(6, this.instance.size()); } @Test void testRemoveAll() { testSize(); - assertTrue(instance.removeAll(asList("A"))); - assertEquals(2, instance.size()); - assertTrue(instance.removeAll(asList("B", "C"))); - assertTrue(instance.isEmpty()); + assertTrue(this.instance.removeAll(asList("A"))); + assertEquals(2, this.instance.size()); + assertTrue(this.instance.removeAll(asList("B", "C"))); + assertTrue(this.instance.isEmpty()); } @Test void testRemoveIf() { testSize(); - assertTrue(instance.removeIf(element -> element.equals("A"))); - assertEquals(2, instance.size()); - assertTrue(instance.removeIf(element -> element.equals("B"))); - assertEquals(1, instance.size()); - assertTrue(instance.removeIf(element -> element.equals("C"))); - assertTrue(instance.isEmpty()); + assertTrue(this.instance.removeIf(element -> element.equals("A"))); + assertEquals(2, this.instance.size()); + assertTrue(this.instance.removeIf(element -> element.equals("B"))); + assertEquals(1, this.instance.size()); + assertTrue(this.instance.removeIf(element -> element.equals("C"))); + assertTrue(this.instance.isEmpty()); } @Test void testRetainAll() { testSize(); - assertTrue(instance.retainAll(asList("A", "B"))); - assertEquals(2, instance.size()); - assertTrue(instance.retainAll(asList("A"))); - assertEquals(1, instance.size()); + assertTrue(this.instance.retainAll(asList("A", "B"))); + assertEquals(2, this.instance.size()); + assertTrue(this.instance.retainAll(asList("A"))); + assertEquals(1, this.instance.size()); } @Test void testClear() { testSize(); - instance.clear(); - assertTrue(instance.isEmpty()); + this.instance.clear(); + assertTrue(this.instance.isEmpty()); } @Test void testEquals() { - assertFalse(instance.equals(new Object())); - assertFalse(instance.equals(ofList("A", "B"))); - assertFalse(instance.equals(ofList("A", "B", "D"))); - assertTrue(instance.equals(new ArrayList<>(instance))); + assertFalse(this.instance.equals(new Object())); + assertFalse(this.instance.equals(ofList("A", "B"))); + assertFalse(this.instance.equals(ofList("A", "B", "D"))); + assertTrue(this.instance.equals(new ArrayList<>(instance))); } @Test void testHashCode() { - assertEquals(instance.hashCode(), new ArrayList<>(instance).hashCode()); + assertEquals(this.instance.hashCode(), new ArrayList<>(instance).hashCode()); - instance.add(null); - assertEquals(instance.hashCode(), new ArrayList<>(instance).hashCode()); + this.instance.add(null); + assertEquals(this.instance.hashCode(), new ArrayList<>(instance).hashCode()); } @Test void testForEach() { - Iterator iterator = instance.iterator(); - instance.forEach(e -> { + Iterator iterator = this.instance.iterator(); + this.instance.forEach(e -> { assertTrue(iterator.hasNext()); assertEquals(iterator.next(), e); }); @@ -208,7 +208,7 @@ void testForEach() { @Test void testToString() { - assertEquals(instance.toString(), new ArrayList<>(instance).toString()); + assertEquals(this.instance.toString(), new ArrayList<>(instance).toString()); } @Test From f78c871257dabc51aac93a5fe6ff3d4c094c4913 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 20:19:54 +0800 Subject: [PATCH 387/438] Add test for remove(null) in ReversedDequeTest Adds a new test that calls instance.remove(null) before invoking super.testRemove(), ensuring the reversed deque handles removal of null without throwing and preserves expected behavior. --- .../java/io/microsphere/collection/ReversedDequeTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ReversedDequeTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ReversedDequeTest.java index 2ad4aa09c..c820661d6 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ReversedDequeTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ReversedDequeTest.java @@ -69,4 +69,10 @@ void testStream() { void testParallelStream() { assertArrayEquals(ofArray("C", "B", "A"), this.instance.parallelStream().toArray()); } + + @Test + void testRemove() { + this.instance.remove(null); + super.testRemove(); + } } \ No newline at end of file From 70100cfe78f27f0f34189d17f5682a53787f9e81 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 20:23:41 +0800 Subject: [PATCH 388/438] Extend BeanUtilsTest and add assertion Make BeanUtilsTest extend LoggingTest (add import) to enable test logging, and add an extra assertion in testGetBeanMetadata to verify that findWriteMethod returns null for a non-existent property name ("not-found"). --- .../src/test/java/io/microsphere/beans/BeanUtilsTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java index 811efb821..3318cfa74 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/beans/BeanUtilsTest.java @@ -18,6 +18,7 @@ package io.microsphere.beans; +import io.microsphere.LoggingTest; import io.microsphere.io.event.FileChangedEvent; import io.microsphere.test.MultipleValueData; import org.junit.jupiter.api.Test; @@ -62,7 +63,7 @@ * @see BeanUtils * @since 1.0.0 */ -class BeanUtilsTest { +class BeanUtilsTest extends LoggingTest { @Test void testGetBeanMetadata() { @@ -91,6 +92,9 @@ void testFindWriteMethod() { BeanMetadata beanMetadata = getBeanMetadata(MultipleValueData.class); Method writeMethod = findWriteMethod(beanMetadata, "stringList"); assertNotNull(writeMethod); + + writeMethod = findWriteMethod(beanMetadata, "not-found"); + assertNull(writeMethod); } @Test From 0d83b5c656ba5cb16eb40bc7b0d6a64ab287339d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 21:02:01 +0800 Subject: [PATCH 389/438] Refactor MutableCollectionTest to use newComparedInstance Replace direct ArrayList usage in tests with a newComparedInstance method that returns a comparable collection (using ListUtils.newArrayList). Fix init to operate on the provided instance parameter. Guard null-element tests with supportsNullElement() and add a default implementation returning true. Update equals, hashCode and toString assertions to use newComparedInstance instead of concrete ArrayList, making the tests generic for different Collection implementations. --- .../collection/MutableCollectionTest.java | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/MutableCollectionTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/MutableCollectionTest.java index 73aa6ca44..50814a77f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/MutableCollectionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/MutableCollectionTest.java @@ -20,10 +20,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import static io.microsphere.collection.ListUtils.newArrayList; import static io.microsphere.collection.Lists.ofList; import static io.microsphere.util.ArrayUtils.EMPTY_OBJECT_ARRAY; import static io.microsphere.util.ArrayUtils.ofArray; @@ -54,10 +54,14 @@ void setUp() { protected abstract C newInstance(); + protected C newComparedInstance(C source) { + return (C) newArrayList(source); + } + protected void init(C instance) { - this.instance.add("A"); - this.instance.add("B"); - this.instance.add("C"); + instance.add("A"); + instance.add("B"); + instance.add("C"); } @Test @@ -122,9 +126,11 @@ void testRemove() { assertTrue(this.instance.remove("B")); assertTrue(this.instance.remove("C")); assertFalse(this.instance.remove("D")); - this.instance.add(null); - assertTrue(this.instance.remove(null)); - assertFalse(this.instance.remove(null)); + if (supportsNullElement()) { + this.instance.add(null); + assertTrue(this.instance.remove(null)); + assertFalse(this.instance.remove(null)); + } } @Test @@ -186,15 +192,16 @@ void testEquals() { assertFalse(this.instance.equals(new Object())); assertFalse(this.instance.equals(ofList("A", "B"))); assertFalse(this.instance.equals(ofList("A", "B", "D"))); - assertTrue(this.instance.equals(new ArrayList<>(instance))); + assertTrue(this.instance.equals(newComparedInstance(instance))); } @Test void testHashCode() { - assertEquals(this.instance.hashCode(), new ArrayList<>(instance).hashCode()); - - this.instance.add(null); - assertEquals(this.instance.hashCode(), new ArrayList<>(instance).hashCode()); + assertEquals(this.instance.hashCode(), newComparedInstance(instance).hashCode()); + if (supportsNullElement()) { + this.instance.add(null); + } + assertEquals(this.instance.hashCode(), newComparedInstance(instance).hashCode()); } @Test @@ -208,7 +215,7 @@ void testForEach() { @Test void testToString() { - assertEquals(this.instance.toString(), new ArrayList<>(instance).toString()); + assertEquals(this.instance.toString(), newComparedInstance(instance).toString()); } @Test @@ -225,4 +232,8 @@ void testStream() { void testParallelStream() { assertArrayEquals(ofArray("A", "B", "C"), this.instance.parallelStream().toArray()); } + + protected boolean supportsNullElement() { + return true; + } } From ba11e73388c6806ffb917a750fc2cca60c0cf054 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 21:02:26 +0800 Subject: [PATCH 390/438] Implement DelegatingWrapper and expose delegate Unwraps potential wrapper delegates in the constructor using Wrapper.tryUnwrap and implements DelegatingWrapper. Adds getDelegate() to expose the underlying BlockingQueue, adds necessary imports, and normalizes usages to this.delegate across methods. This prevents double-wrapping and makes the wrapped queue available for callers/inspection. --- .../concurrent/DelegatingBlockingQueue.java | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/concurrent/DelegatingBlockingQueue.java b/microsphere-java-core/src/main/java/io/microsphere/concurrent/DelegatingBlockingQueue.java index d9c41bbf6..53498cbd3 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/concurrent/DelegatingBlockingQueue.java +++ b/microsphere-java-core/src/main/java/io/microsphere/concurrent/DelegatingBlockingQueue.java @@ -16,6 +16,9 @@ */ package io.microsphere.concurrent; +import io.microsphere.lang.DelegatingWrapper; +import io.microsphere.lang.Wrapper; + import java.util.Collection; import java.util.Iterator; import java.util.Spliterator; @@ -47,57 +50,58 @@ * @author Mercy * @since 1.0.0 */ -public class DelegatingBlockingQueue implements BlockingQueue { +public class DelegatingBlockingQueue implements BlockingQueue, DelegatingWrapper { private final BlockingQueue delegate; public DelegatingBlockingQueue(BlockingQueue delegate) { - this.delegate = delegate; + BlockingQueue unwrapper = Wrapper.tryUnwrap(delegate, BlockingQueue.class); + this.delegate = unwrapper == null ? delegate : unwrapper; } @Override public Iterator iterator() { - return delegate.iterator(); + return this.delegate.iterator(); } @Override public Object[] toArray() { - return delegate.toArray(); + return this.delegate.toArray(); } @Override public T[] toArray(T[] a) { - return delegate.toArray(a); + return this.delegate.toArray(a); } @Override public boolean containsAll(Collection c) { - return delegate.containsAll(c); + return this.delegate.containsAll(c); } @Override public boolean addAll(Collection c) { - return delegate.addAll(c); + return this.delegate.addAll(c); } @Override public boolean removeAll(Collection c) { - return delegate.removeAll(c); + return this.delegate.removeAll(c); } @Override public boolean removeIf(Predicate filter) { - return delegate.removeIf(filter); + return this.delegate.removeIf(filter); } @Override public boolean retainAll(Collection c) { - return delegate.retainAll(c); + return this.delegate.retainAll(c); } @Override public void clear() { - delegate.clear(); + this.delegate.clear(); } @Override @@ -112,121 +116,126 @@ public boolean equals(Object o) { DelegatingBlockingQueue that = (DelegatingBlockingQueue) o; return this.delegate.equals(that.delegate); } - return delegate.equals(o); + return this.delegate.equals(o); } @Override public int hashCode() { - return delegate.hashCode(); + return this.delegate.hashCode(); } @Override public Spliterator spliterator() { - return delegate.spliterator(); + return this.delegate.spliterator(); } @Override public Stream stream() { - return delegate.stream(); + return this.delegate.stream(); } @Override public Stream parallelStream() { - return delegate.parallelStream(); + return this.delegate.parallelStream(); } @Override public boolean add(E e) { - return delegate.add(e); + return this.delegate.add(e); } @Override public boolean offer(E e) { - return delegate.offer(e); + return this.delegate.offer(e); } @Override public void put(E e) throws InterruptedException { - delegate.put(e); + this.delegate.put(e); } @Override public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - return delegate.offer(e, timeout, unit); + return this.delegate.offer(e, timeout, unit); } @Override public E take() throws InterruptedException { - return delegate.take(); + return this.delegate.take(); } @Override public E poll(long timeout, TimeUnit unit) throws InterruptedException { - return delegate.poll(timeout, unit); + return this.delegate.poll(timeout, unit); } @Override public int remainingCapacity() { - return delegate.remainingCapacity(); + return this.delegate.remainingCapacity(); } @Override public boolean remove(Object o) { - return delegate.remove(o); + return this.delegate.remove(o); } @Override public boolean contains(Object o) { - return delegate.contains(o); + return this.delegate.contains(o); } @Override public int drainTo(Collection c) { - return delegate.drainTo(c); + return this.delegate.drainTo(c); } @Override public int drainTo(Collection c, int maxElements) { - return delegate.drainTo(c, maxElements); + return this.delegate.drainTo(c, maxElements); } @Override public E remove() { - return delegate.remove(); + return this.delegate.remove(); } @Override public E poll() { - return delegate.poll(); + return this.delegate.poll(); } @Override public E element() { - return delegate.element(); + return this.delegate.element(); } @Override public E peek() { - return delegate.peek(); + return this.delegate.peek(); } @Override public int size() { - return delegate.size(); + return this.delegate.size(); } @Override public boolean isEmpty() { - return delegate.isEmpty(); + return this.delegate.isEmpty(); } @Override public void forEach(Consumer action) { - delegate.forEach(action); + this.delegate.forEach(action); } @Override public String toString() { - return delegate.toString(); + return this.delegate.toString(); + } + + @Override + public Object getDelegate() { + return this.delegate; } } From 91b43890a9b1fbe52d6f2a9808f5b02c5cae6f7d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 21:02:34 +0800 Subject: [PATCH 391/438] Use MutableQueueTest in DelegatingBlockingQueueTest Refactors DelegatingBlockingQueueTest to extend MutableQueueTest to reuse common queue test behavior. Adds import for MutableQueueTest, overrides newInstance, newComparedInstance and supportsNullElement (returns false), and adjusts queue initialization to reference this.delegate. Also clears the queue at the start of the test to ensure a clean state. --- .../DelegatingBlockingQueueTest.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingBlockingQueueTest.java b/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingBlockingQueueTest.java index 421707ff3..37de437cf 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingBlockingQueueTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/concurrent/DelegatingBlockingQueueTest.java @@ -1,5 +1,6 @@ package io.microsphere.concurrent; +import io.microsphere.collection.MutableQueueTest; import org.junit.jupiter.api.Test; import java.util.Iterator; @@ -30,14 +31,30 @@ * @see DelegatingBlockingQueue * @since 1.0.0 */ -class DelegatingBlockingQueueTest { +class DelegatingBlockingQueueTest extends MutableQueueTest> { private BlockingQueue delegate = new LinkedBlockingDeque<>(); - private DelegatingBlockingQueue queue = new DelegatingBlockingQueue<>(delegate); + private DelegatingBlockingQueue queue = new DelegatingBlockingQueue<>(this.delegate); + + @Override + protected DelegatingBlockingQueue newInstance() { + return this.queue; + } + + @Override + protected DelegatingBlockingQueue newComparedInstance(DelegatingBlockingQueue source) { + return new DelegatingBlockingQueue(source); + } + + @Override + protected boolean supportsNullElement() { + return false; + } @Test void test() throws Throwable { + this.queue.clear(); // test add assertTrue(queue.add(1)); From 4e77dbda02626068f9b2b29c9e8d9aa0f394aa3e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 21:09:58 +0800 Subject: [PATCH 392/438] Remove extra blank line in ArtifactDetector Whitespace cleanup in ArtifactDetector.java: removed an unnecessary blank line before the class closing brace and adjusted the file ending (no functional changes). Improves file formatting/consistency. --- .../java/io/microsphere/classloading/ArtifactDetector.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/ArtifactDetector.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/ArtifactDetector.java index b4b09622b..8ab08c4d1 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/ArtifactDetector.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/ArtifactDetector.java @@ -152,5 +152,4 @@ private void removeJdkClassPathURLs(Set classPathURLs) { } } } - -} +} \ No newline at end of file From 72ce0b8f74be24e47aa075d2bf164b7296993414 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 21:12:41 +0800 Subject: [PATCH 393/438] Use LoggingTest and explicit classLoader in test Replace AbstractTestCase with LoggingTest in ServiceLoaderUtilsTest and add a static import for ClassLoaderUtils.getClassLoader. Introduce a classLoader field initialized with getClassLoader(ServiceLoaderUtilsTest.class) so the test uses an explicit class loader. Also includes a trivial EOF newline change in ServiceLoaderUtils.java. --- .../main/java/io/microsphere/util/ServiceLoaderUtils.java | 2 +- .../java/io/microsphere/util/ServiceLoaderUtilsTest.java | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ServiceLoaderUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ServiceLoaderUtils.java index fe5145f48..041d6747f 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ServiceLoaderUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ServiceLoaderUtils.java @@ -610,4 +610,4 @@ static List loadServicesAsList(Class serviceType, @Nullable ClassLoade private ServiceLoaderUtils() { } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ServiceLoaderUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ServiceLoaderUtilsTest.java index 6e861ab18..ac1cc4628 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ServiceLoaderUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ServiceLoaderUtilsTest.java @@ -1,6 +1,6 @@ package io.microsphere.util; -import io.microsphere.AbstractTestCase; +import io.microsphere.LoggingTest; import io.microsphere.event.EchoEventListener; import io.microsphere.event.EchoEventListener2; import io.microsphere.event.EventListener; @@ -10,6 +10,7 @@ import java.util.Set; import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.util.ClassLoaderUtils.getClassLoader; import static io.microsphere.util.ServiceLoaderUtils.SERVICE_LOADER_CACHED; import static io.microsphere.util.ServiceLoaderUtils.loadFirstService; import static io.microsphere.util.ServiceLoaderUtils.loadLastService; @@ -26,12 +27,14 @@ * @see ServiceLoaderUtilsTest * @since 1.0.0 */ -class ServiceLoaderUtilsTest extends AbstractTestCase { +class ServiceLoaderUtilsTest extends LoggingTest { private static final Class TEST_CLASS = EventListener.class; private static final boolean TEST_CACHED = SERVICE_LOADER_CACHED; + private static final ClassLoader classLoader = getClassLoader(ServiceLoaderUtilsTest.class); + @Test void testLoadServicesListWithServiceType() { List eventListeners = loadServicesList(TEST_CLASS); From db292ffbc2da26f44c3de2b682ebdebabde5e1fc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 21:34:19 +0800 Subject: [PATCH 394/438] Use instanceof in Task.equals; update tests Replace the getClass() check with an instanceof check in Task.equals to allow equality checks with subclasses and improve readability. Update StopWatchTest to consistently use this.stopWatch, fix formatting, and add a new test (testGetCurrentTask) to assert getCurrentTask behavior (including reentrant lookup and null when no task is running). Minor test coverage and clarity improvements. --- .../java/io/microsphere/util/StopWatch.java | 8 ++- .../io/microsphere/util/StopWatchTest.java | 55 ++++++++++++------- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/StopWatch.java b/microsphere-java-core/src/main/java/io/microsphere/util/StopWatch.java index 9c8c2118e..2d4b15db3 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/StopWatch.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/StopWatch.java @@ -226,8 +226,12 @@ public long getElapsedNanos() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (!(o instanceof Task)) { + return false; + } Task task = (Task) o; return Objects.equals(taskName, task.taskName); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java index 55bdb6782..d586aec84 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/StopWatchTest.java @@ -49,30 +49,30 @@ class StopWatchTest implements Loggable { @BeforeEach void setUp() { - stopWatch = new StopWatch(testName); + this.stopWatch = new StopWatch(testName); } @Test void test() throws InterruptedException { - stopWatch.start("1"); + this.stopWatch.start("1"); sleep(100); - stopWatch.start("2"); + this.stopWatch.start("2"); sleep(10); - stopWatch.stop(); - stopWatch.stop(); - Task currentTask = stopWatch.getCurrentTask(); + this.stopWatch.stop(); + this.stopWatch.stop(); + Task currentTask = this.stopWatch.getCurrentTask(); assertNull(currentTask); - assertEquals(testName, stopWatch.getId()); - assertEquals(0, stopWatch.getRunningTasks().size()); - assertEquals(2, stopWatch.getCompletedTasks().size()); - Task task = stopWatch.getCompletedTasks().get(1); + assertEquals(testName, this.stopWatch.getId()); + assertEquals(0, this.stopWatch.getRunningTasks().size()); + assertEquals(2, this.stopWatch.getCompletedTasks().size()); + Task task = this.stopWatch.getCompletedTasks().get(1); assertEquals("1", task.getTaskName()); assertFalse(task.isReentrant()); assertTrue(task.getStartTimeNanos() > 0); assertTrue(task.getElapsedNanos() > 0); - assertTrue(stopWatch.getTotalTimeNanos() > 0); - assertTrue(stopWatch.getTotalTime(MILLISECONDS) > 0); - log(stopWatch.toString()); + assertTrue(this.stopWatch.getTotalTimeNanos() > 0); + assertTrue(this.stopWatch.getTotalTime(MILLISECONDS) > 0); + log(this.stopWatch.toString()); } @Test @@ -106,35 +106,48 @@ void testTaskHashCode() { @Test void testStartOnNullTaskName() { - assertThrows(IllegalArgumentException.class, () -> stopWatch.start(null)); + assertThrows(IllegalArgumentException.class, () -> this.stopWatch.start(null)); } @Test void testStartOnEmptyTaskName() { - assertThrows(IllegalArgumentException.class, () -> stopWatch.start("")); + assertThrows(IllegalArgumentException.class, () -> this.stopWatch.start("")); } @Test void testStartOnBlankTaskName() { - assertThrows(IllegalArgumentException.class, () -> stopWatch.start(SPACE)); + assertThrows(IllegalArgumentException.class, () -> this.stopWatch.start(SPACE)); } @Test void testStartOnAlreadyRunning() { assertThrows(IllegalStateException.class, () -> { - stopWatch.start("1"); - stopWatch.start("1"); + this.stopWatch.start("1"); + this.stopWatch.start("1"); }); } @Test void testStartOnReentrant() { - stopWatch.start("1", true); - stopWatch.start("1"); + this.stopWatch.start("1", true); + this.stopWatch.start("1"); } @Test void testStopOnNoTaskRunning() { - assertThrows(IllegalStateException.class, stopWatch::stop); + assertThrows(IllegalStateException.class, this.stopWatch::stop); + } + + @Test + void testGetCurrentTask() { + this.stopWatch.start("1", true); + Task currentTask = this.stopWatch.getCurrentTask(); + assertEquals("1", currentTask.getTaskName()); + + currentTask = this.stopWatch.getCurrentTask(true); + assertEquals("1", currentTask.getTaskName()); + + assertNull(this.stopWatch.getCurrentTask()); + assertNull(this.stopWatch.getCurrentTask(true)); } } From 3cabd0a94acd65db17185121665dd0b65bfd8b02 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 21:36:14 +0800 Subject: [PATCH 395/438] Extend SystemUtilsTest with LoggingTest Import io.microsphere.LoggingTest and make SystemUtilsTest extend LoggingTest to enable shared logging/test utilities for the class. No other functional changes were made. --- .../src/test/java/io/microsphere/util/SystemUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/SystemUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/SystemUtilsTest.java index 6285786fd..8eaf0d650 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/SystemUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/SystemUtilsTest.java @@ -16,6 +16,7 @@ */ package io.microsphere.util; +import io.microsphere.LoggingTest; import io.microsphere.reflect.MemberUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; @@ -108,7 +109,7 @@ * @see 1.0.0 * @since 1.0.0 */ -class SystemUtilsTest { +class SystemUtilsTest extends LoggingTest { private static final Class CLASS = SystemUtils.class; From 8ede298299152d3102634c6d7b3cd1e113b1a4de Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 3 Mar 2026 21:41:12 +0800 Subject: [PATCH 396/438] Add reversed(MethodHandle) overload Make DelegatingDeque.reversed() delegate to a new protected reversed(MethodHandle) overload so a MethodHandle can be supplied (e.g. null) to control behavior. Update DelegatingDequeTest to call reversed(null) and assert the reversed ordering, covering the branch where the method handle is provided or absent. --- .../main/java/io/microsphere/collection/DelegatingDeque.java | 4 ++++ .../java/io/microsphere/collection/DelegatingDequeTest.java | 3 +++ 2 files changed, 7 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/collection/DelegatingDeque.java b/microsphere-java-core/src/main/java/io/microsphere/collection/DelegatingDeque.java index 785324069..6c4b365bc 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/collection/DelegatingDeque.java +++ b/microsphere-java-core/src/main/java/io/microsphere/collection/DelegatingDeque.java @@ -136,6 +136,10 @@ public Deque getDelegate() { * @since Java 21 */ public Deque reversed() { + return reversed(reversedMethodHandle); + } + + protected Deque reversed(MethodHandle reversedMethodHandle) { if (reversedMethodHandle == null) { return reversedDeque(getDelegate()); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingDequeTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingDequeTest.java index 3858dbb77..eec9d7bcb 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingDequeTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/DelegatingDequeTest.java @@ -48,6 +48,9 @@ void testReversed() { DelegatingDeque delegatingDeque = (DelegatingDeque) this.instance; Deque reversedDeque = delegatingDeque.reversed(); assertArrayEquals(ofArray("C", "B", "A"), reversedDeque.toArray()); + + reversedDeque = delegatingDeque.reversed(null); + assertArrayEquals(ofArray("C", "B", "A"), reversedDeque.toArray()); } @Test From f69f5ab74423b68fc714dce11ce4e8da1eeca46a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 08:53:44 +0800 Subject: [PATCH 397/438] Refactor ParameterizedTypeImpl and add tests Improve ParameterizedTypeImpl equality, hashing and toString logic: add fast-path this==o check, normalize field references with this., use ArrayUtils.isNotEmpty for argument checks, and simplify return flow. Update hashCode to reference instance fields consistently. Add unit tests: compare Dictionary vs Map parameterized types and verify toString() for simple types. Minor import adjustments to support changes. --- .../generics/ParameterizedTypeImpl.java | 37 +++++++++---------- .../generics/ParameterizedTypeImplTest.java | 7 ++++ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/generics/ParameterizedTypeImpl.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/generics/ParameterizedTypeImpl.java index f6cc89cfb..50b82b1f8 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/generics/ParameterizedTypeImpl.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/generics/ParameterizedTypeImpl.java @@ -24,6 +24,7 @@ import static io.microsphere.constants.SymbolConstants.DOLLAR_CHAR; import static io.microsphere.util.ArrayUtils.arrayEquals; +import static io.microsphere.util.ArrayUtils.isNotEmpty; import static java.util.Objects.hash; /** @@ -180,44 +181,42 @@ public Type getOwnerType() { */ @Override public boolean equals(Object o) { + if (this == o) { + return true; + } if (o instanceof ParameterizedType) { // Check that information is equivalent ParameterizedType that = (ParameterizedType) o; - if (this == that) - return true; - Type thatOwner = that.getOwnerType(); Type thatRawType = that.getRawType(); - return - Objects.equals(ownerType, thatOwner) && - Objects.equals(rawType, thatRawType) && - arrayEquals(actualTypeArguments, // avoid clone - that.getActualTypeArguments()); - } else - return false; + return Objects.equals(this.ownerType, thatOwner) + && Objects.equals(this.rawType, thatRawType) + && arrayEquals(this.actualTypeArguments, that.getActualTypeArguments()); + } + return false; } @Override public int hashCode() { - return hash(actualTypeArguments) ^ - Objects.hashCode(ownerType) ^ - Objects.hashCode(rawType); + return hash(this.actualTypeArguments) ^ + Objects.hashCode(this.ownerType) ^ + Objects.hashCode(this.rawType); } public String toString() { StringBuilder sb = new StringBuilder(); - if (ownerType != null) { - if (ownerType instanceof Class) - sb.append(((Class) ownerType).getName()); + if (this.ownerType != null) { + if (this.ownerType instanceof Class) + sb.append(((Class) this.ownerType).getName()); else - sb.append(ownerType); + sb.append(this.ownerType); sb.append(DOLLAR_CHAR); - if (ownerType instanceof ParameterizedTypeImpl) { + if (this.ownerType instanceof ParameterizedTypeImpl) { // Find simple name of nested type by removing the // shared prefix with owner. sb.append(rawType.getName().replace(((ParameterizedTypeImpl) ownerType).rawType.getName() + DOLLAR_CHAR, "")); @@ -226,7 +225,7 @@ public String toString() { } else sb.append(rawType.getName()); - if (actualTypeArguments != null && actualTypeArguments.length > 0) { + if (isNotEmpty(actualTypeArguments)) { sb.append("<"); boolean first = true; for (Type t : actualTypeArguments) { diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/generics/ParameterizedTypeImplTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/generics/ParameterizedTypeImplTest.java index 8ca491fa2..7f527a6ce 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/generics/ParameterizedTypeImplTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/generics/ParameterizedTypeImplTest.java @@ -6,6 +6,7 @@ import java.lang.reflect.MalformedParameterizedTypeException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Dictionary; import java.util.Map; import static io.microsphere.reflect.generics.ParameterizedTypeImpl.of; @@ -92,6 +93,7 @@ void testEqualsOnSameType() { @Test void testEqualsOnDifferentType() { assertNotEquals(topLevelType, of(Map.class, actualTypeArguments, String.class)); + assertNotEquals(of(Dictionary.class, actualTypeArguments, String.class), of(Map.class, actualTypeArguments, String.class)); assertNotEquals(topLevelType, nestedType); assertNotEquals(topLevelType, complexType); assertNotEquals(topLevelType, Object.class); @@ -138,4 +140,9 @@ void testToStringOnDifferentType() { assertNotEquals(topLevelType.toString(), nestedType.toString()); assertNotEquals(topLevelType.toString(), complexType.toString()); } + + @Test + void testToString() { + assertEquals("java.lang.String", of(String.class).toString()); + } } \ No newline at end of file From 9c0c682b67f5949d913a4a6fd5e71cb155d9c17f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 09:27:26 +0800 Subject: [PATCH 398/438] Extract file-entry check to helper method Refactor SimpleJarEntryScanner: remove direct PathConstants usage, add static import of SLASH, and extract the inline logic that determined whether a JarEntry is a file into a new isFileEntry(String,String) helper. This improves readability and isolates the entry-check logic without changing behavior. --- .../io/scanner/SimpleJarEntryScanner.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleJarEntryScanner.java b/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleJarEntryScanner.java index 66ad8773b..d97e44a9c 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleJarEntryScanner.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleJarEntryScanner.java @@ -5,7 +5,6 @@ import io.microsphere.annotation.Immutable; import io.microsphere.annotation.Nonnull; -import io.microsphere.constants.PathConstants; import io.microsphere.filter.JarEntryFilter; import io.microsphere.util.jar.JarUtils; @@ -17,6 +16,7 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; +import static io.microsphere.constants.PathConstants.SLASH; import static io.microsphere.util.StringUtils.EMPTY; import static io.microsphere.util.jar.JarUtils.filter; import static io.microsphere.util.jar.JarUtils.resolveRelativePath; @@ -142,10 +142,7 @@ protected Set scan(JarFile jarFile, String relativePath, final boolean if (jarEntry.isDirectory()) { accept = jarEntryName.equals(relativePath); } else { - int beginIndex = jarEntryName.indexOf(relativePath); - if (beginIndex == 0) { - accept = jarEntryName.indexOf(PathConstants.SLASH, relativePath.length()) < 0; - } + accept = isFileEntry(jarEntryName, relativePath); } } if (accept) { @@ -154,4 +151,12 @@ protected Set scan(JarFile jarFile, String relativePath, final boolean } return unmodifiableSet(jarEntriesSet); } + + boolean isFileEntry(String jarEntryName, String relativePath) { + int beginIndex = jarEntryName.indexOf(relativePath); + if (beginIndex == 0) { + return jarEntryName.indexOf(SLASH, relativePath.length()) < 0; + } + return false; + } } From 5e34364a7b0adc576af9609a2c9227937dcf921b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 09:27:32 +0800 Subject: [PATCH 399/438] Add test for SimpleJarEntryScanner.isFileEntry Adds a unit test (testIsFileEntry) in SimpleJarEntryScannerTest to verify isFileEntry behavior: it should return true when the directory prefix includes a trailing slash and false when the prefix lacks the trailing slash. Also adds the necessary assertFalse import. This ensures correct handling of directory prefixes when identifying file entries in JAR scanning. --- .../microsphere/io/scanner/SimpleJarEntryScannerTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleJarEntryScannerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleJarEntryScannerTest.java index 2102f836e..6bc50d44e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleJarEntryScannerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleJarEntryScannerTest.java @@ -18,6 +18,7 @@ import static io.microsphere.util.ClassLoaderUtils.getClassResource; import static io.microsphere.util.ClassLoaderUtils.getResource; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -53,4 +54,10 @@ void testScanInJarFile() throws IOException { jarEntrySet = simpleJarEntryScanner.scan(jarFile, true, jarEntry -> jarEntry.getName().equals("javax/annotation/Nonnull.class")); assertEquals(1, jarEntrySet.size()); } + + @Test + void testIsFileEntry() { + assertTrue(simpleJarEntryScanner.isFileEntry("java/lang/String.class", "java/lang/")); + assertFalse(simpleJarEntryScanner.isFileEntry("java/lang/String.class", "java/lang")); + } } From 3b65d6025a0bb5426754a8b6b7093986abdcc0af Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 10:36:21 +0800 Subject: [PATCH 400/438] Add inequality test for differing type arguments Add an assertion in ParameterizedTypeImplTest.testEqualsOnDifferentType to verify that two ParameterizedType instances with the same raw type but different actual type arguments are not considered equal (comparing Map with original actualTypeArguments vs a Map with two String type arguments). This strengthens equality checks for parameterized types. --- .../microsphere/reflect/generics/ParameterizedTypeImplTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/generics/ParameterizedTypeImplTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/generics/ParameterizedTypeImplTest.java index 7f527a6ce..4049e5c91 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/generics/ParameterizedTypeImplTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/generics/ParameterizedTypeImplTest.java @@ -94,6 +94,7 @@ void testEqualsOnSameType() { void testEqualsOnDifferentType() { assertNotEquals(topLevelType, of(Map.class, actualTypeArguments, String.class)); assertNotEquals(of(Dictionary.class, actualTypeArguments, String.class), of(Map.class, actualTypeArguments, String.class)); + assertNotEquals(of(Map.class, actualTypeArguments, String.class), of(Map.class, new Type[]{String.class, String.class}, String.class)); assertNotEquals(topLevelType, nestedType); assertNotEquals(topLevelType, complexType); assertNotEquals(topLevelType, Object.class); From 126c70f9dfc66b107d6d9019e77a8b21b628e1ed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 10:39:09 +0800 Subject: [PATCH 401/438] Add self-equality test and fix class whitespace In ArtifactTest, added an assertion to verify equals is reflexive (assertTrue(artifact.equals(artifact))). Also removed an extra space in the class declaration (class ArtifactTest {). Improves test coverage for the equals contract and cleans up minor formatting. --- .../test/java/io/microsphere/classloading/ArtifactTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactTest.java index 03c6f6baf..3196b2c1b 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/ArtifactTest.java @@ -40,7 +40,7 @@ * @see Artifact * @since 1.0.0 */ -class ArtifactTest { +class ArtifactTest { private static final String ARTIFACT_ID = "microsphere-core"; @@ -81,6 +81,7 @@ void testCreateOnArtifactIdAndVersionAndLocation() { @Test void testEquals() { + assertTrue(artifact.equals(artifact)); assertTrue(artifact.equals(create(ARTIFACT_ID, VERSION, LOCATION))); assertFalse(artifact.equals(null)); From 98fe494b412d5e6a7b07d32fa274b2c1fc1d907e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 10:42:58 +0800 Subject: [PATCH 402/438] Add test for Functional.on null handling Adds a unit test (testNullValue) to verify that Functional.on with Objects::nonNull returns the same Functional instance when the supplier yields null. Also adds necessary imports (Objects and assertSame). This ensures null values don't create unexpected chained behavior in Functional.on. --- .../test/java/io/microsphere/util/FunctionalTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/FunctionalTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/FunctionalTest.java index aa637462d..89f31a0d6 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/FunctionalTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/FunctionalTest.java @@ -18,10 +18,13 @@ import org.junit.jupiter.api.Test; +import java.util.Objects; + import static io.microsphere.util.Functional.of; import static io.microsphere.util.Functional.value; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link Functional} Test @@ -68,6 +71,12 @@ void testOfWithNameAndValueSupplier() { assertValue(of("test", () -> 1).on(v -> v > 0).as(String::valueOf), "1"); } + @Test + void testNullValue() { + Functional test = of("test", () -> null); + assertSame(test, test.on(Objects::nonNull)); + } + private void assertValue(Functional functional, T expected) { functional.apply(valueHolder::setValue); assertEquals(expected, valueHolder.getValue()); From 453093a132d1685d7d76d9584c1071edde2b0892 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 11:08:29 +0800 Subject: [PATCH 403/438] Add ReflectiveDefinition test classes Add test support for ReflectiveDefinition: introduce ReflectiveDefinitionImpl (test subclass with multiple constructors and isPresent() returning true) and ReflectiveDefinitionTest (extends AbstractReflectiveDefinitionTest and supplies empty tail constructor arguments). Both files are added under microsphere-java-core/src/test/java/io/microsphere/reflect and include Apache license headers. --- .../reflect/ReflectiveDefinitionImpl.java | 54 +++++++++++++++++++ .../reflect/ReflectiveDefinitionTest.java | 38 +++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionImpl.java create mode 100644 microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionImpl.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionImpl.java new file mode 100644 index 000000000..2a8908dc0 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionImpl.java @@ -0,0 +1,54 @@ +/* + * 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. + */ + +package io.microsphere.reflect; + +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.lang.Deprecation; +import io.microsphere.util.Version; + +/** + * {@link ReflectiveDefinition} Implementation class + * + * @author Mercy + * @see ReflectiveDefinition + * @since 1.0.0 + */ +public class ReflectiveDefinitionImpl extends ReflectiveDefinition { + + public ReflectiveDefinitionImpl(@Nonnull String since, @Nonnull String className) { + super(since, className); + } + + public ReflectiveDefinitionImpl(@Nonnull String since, @Nullable Deprecation deprecation, @Nonnull String className) { + super(since, deprecation, className); + } + + public ReflectiveDefinitionImpl(@Nonnull Version since, @Nonnull String className) { + super(since, className); + } + + public ReflectiveDefinitionImpl(@Nonnull Version since, @Nullable Deprecation deprecation, @Nonnull String className) { + super(since, deprecation, className); + } + + @Override + public boolean isPresent() { + return true; + } +} diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java new file mode 100644 index 000000000..19f49318d --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.microsphere.reflect; + + +import java.util.List; + +import static java.util.Collections.emptyList; + +/** + * {@link ReflectiveDefinition} Test + * + * @author Mercy + * @see ReflectiveDefinition + * @since 1.0.0 + */ +class ReflectiveDefinitionTest extends AbstractReflectiveDefinitionTest { + + @Override + protected List getTailConstructorArguments() { + return emptyList(); + } +} \ No newline at end of file From 55b1b150281ef396cb317ed99fb1d18ce2526d74 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 12:38:58 +0800 Subject: [PATCH 404/438] Add assertPackage(String) and update tests Extract package validation into a new assertPackage(String) overload and have assertPackage(Class) delegate to it. Update unit tests to cover new behaviors: added tests for assertPackage error cases, reformSpec result, and openConnection handling when a null-producing factory is present. Added test helpers (FalseSubProtocolURLConnectionFactory, NullSubProtocolURLConnectionFactory), adjusted imports, and made MemberClass static to support testing. --- .../ExtendableProtocolURLStreamHandler.java | 5 +- ...xtendableProtocolURLStreamHandlerTest.java | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/ExtendableProtocolURLStreamHandler.java b/microsphere-java-core/src/main/java/io/microsphere/net/ExtendableProtocolURLStreamHandler.java index 91ae079cd..6a6d8c6be 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/ExtendableProtocolURLStreamHandler.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/ExtendableProtocolURLStreamHandler.java @@ -196,7 +196,10 @@ static void assertClassName(Class type) { } static void assertPackage(Class type) { - String className = type.getName(); + assertPackage(type.getName()); + } + + static void assertPackage(String className) { if (className.indexOf(DOT_CHAR) < 0) { throw new IllegalArgumentException("The Handler class must not be present at the top package!"); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/ExtendableProtocolURLStreamHandlerTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/ExtendableProtocolURLStreamHandlerTest.java index 9454d0092..634942027 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/ExtendableProtocolURLStreamHandlerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/ExtendableProtocolURLStreamHandlerTest.java @@ -22,7 +22,10 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.Proxy; import java.net.URL; +import java.net.URLConnection; import java.util.List; import static io.microsphere.collection.ListUtils.newLinkedList; @@ -43,6 +46,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -151,6 +155,17 @@ void testOpenConnection() throws IOException { URL url = new URL(TEST_URL); assertNull(url.openConnection()); assertEquals(TEST_URL, url.toString()); + + } + + @Test + void testOpenConnectionOnNull() throws IOException { + URL url = new URL(TEST_URL); + this.handler.customizeSubProtocolURLConnectionFactories(factories -> { + factories.add(0, new FalseSubProtocolURLConnectionFactory()); + factories.add(1, new NullSubProtocolURLConnectionFactory()); + }); + assertNull(this.handler.openConnection(url)); } @Test @@ -234,6 +249,46 @@ void testToString() { assertEquals("io.microsphere.net.test.Handler {defaultPort = -1 , protocol = 'test'}", handler.toString()); } + @Test + void testAssertPackage() { + assertThrows(IllegalArgumentException.class, () -> assertPackage("")); + assertThrows(IllegalArgumentException.class, () -> assertPackage("Test")); + } + + @Test + void testReformSpec() throws MalformedURLException { + String spec = TEST_URL_WITH_SP + "?a=b"; + URL url = new URL(spec); + String s = this.handler.reformSpec(url, spec, 0, 1, spec.length()); + assertNotNull(s); + } + private static class MemberClass extends ExtendableProtocolURLStreamHandler { } + + static class FalseSubProtocolURLConnectionFactory implements SubProtocolURLConnectionFactory { + + @Override + public boolean supports(URL url, List subProtocols) { + return false; + } + + @Override + public URLConnection create(URL url, List subProtocols, Proxy proxy) throws IOException { + return null; + } + } + + static class NullSubProtocolURLConnectionFactory implements SubProtocolURLConnectionFactory { + + @Override + public boolean supports(URL url, List subProtocols) { + return true; + } + + @Override + public URLConnection create(URL url, List subProtocols, Proxy proxy) throws IOException { + return null; + } + } } From 0faba5b2ca68222708e037acf5f6c360977bf2be Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 12:41:45 +0800 Subject: [PATCH 405/438] Add self-equality and null-matches tests Enhance MavenArtifactTest by adding two assertions: verify that an artifact equals itself (self-equality) and that matches(...) returns false when given null. These additions improve test coverage and ensure equals/matches handle self and null cases correctly. --- .../java/io/microsphere/classloading/MavenArtifactTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/MavenArtifactTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/MavenArtifactTest.java index b45be1ccc..998c0e2c3 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/MavenArtifactTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/MavenArtifactTest.java @@ -86,6 +86,7 @@ void testCreateOnGroupIdAndArtifactIdAndVersionAndLocation() { @Test void testEquals() { + assertTrue(artifact.equals(artifact)); assertTrue(artifact.equals(create(GROUP_ID, ARTIFACT_ID, VERSION, LOCATION))); assertFalse(artifact.equals(null)); @@ -116,5 +117,6 @@ void testMatches() { assertFalse(create(HYPHEN, ARTIFACT_ID, VERSION).matches(artifact)); assertFalse(create(GROUP_ID, HYPHEN, VERSION).matches(artifact)); assertFalse(create(GROUP_ID, ARTIFACT_ID, HYPHEN).matches(artifact)); + assertFalse(create(GROUP_ID, ARTIFACT_ID, HYPHEN).matches(null)); } } From 217d4dd34bc5e4e5ae9ddd5b85c6561b775128d5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 14:26:26 +0800 Subject: [PATCH 406/438] Use LoggingTest and add classLoader field Switch the test base class from AbstractTestCase to LoggingTest and add a protected final classLoader field initialized via ClassLoaderUtils.getClassLoader(this.getClass()). Also add the required static import for getClassLoader. This prepares the test to use logging utilities and provides a reusable ClassLoader reference. --- .../classloading/BaseURLClassPathHandleTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/BaseURLClassPathHandleTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/BaseURLClassPathHandleTest.java index be465578d..7750e818e 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/BaseURLClassPathHandleTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/BaseURLClassPathHandleTest.java @@ -16,7 +16,7 @@ */ package io.microsphere.classloading; -import io.microsphere.AbstractTestCase; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -25,6 +25,7 @@ import static io.microsphere.net.URLUtils.EMPTY_URL_ARRAY; import static io.microsphere.net.URLUtils.ofURL; +import static io.microsphere.util.ClassLoaderUtils.getClassLoader; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -37,10 +38,12 @@ * @since 1.0.0 */ @Disabled -abstract class BaseURLClassPathHandleTest extends AbstractTestCase { +abstract class BaseURLClassPathHandleTest extends LoggingTest { protected H handle; + protected final ClassLoader classLoader = getClassLoader(this.getClass()); + @BeforeEach void setUp() { this.handle = createHandle(); From abbd8b290da7dbe2d38e2c4fabe0d7b147dba8ee Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 14:26:52 +0800 Subject: [PATCH 407/438] Rename redundant test method names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove redundant 'Test' duplication from method names in AbstractReflectiveDefinitionTest: testTestEquals → testEquals, testTestHashCode → testHashCode, and testTestToString → testToString. No logic or assertions were changed; this improves test method naming clarity. --- .../reflect/AbstractReflectiveDefinitionTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java index f1c3724fc..48b3095af 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java @@ -123,7 +123,7 @@ void testIsPresent() { } @Test - void testTestEquals() { + void testEquals() { assertEquals(definitions.get(0), definitions.get(0)); assertEquals(definitions.get(0), definitions.get(1)); assertEquals(definitions.get(2), definitions.get(3)); @@ -134,7 +134,7 @@ void testTestEquals() { } @Test - void testTestHashCode() { + void testHashCode() { assertEquals(definitions.get(0).hashCode(), definitions.get(1).hashCode()); assertEquals(definitions.get(2).hashCode(), definitions.get(3).hashCode()); assertNotEquals(definitions.get(0).hashCode(), definitions.get(2).hashCode()); @@ -142,7 +142,7 @@ void testTestHashCode() { } @Test - void testTestToString() { + void testToString() { assertEquals(definitions.get(0).toString(), definitions.get(1).toString()); assertEquals(definitions.get(2).toString(), definitions.get(3).toString()); assertNotEquals(definitions.get(0).toString(), definitions.get(2).toString()); From 27e68c1d2faf4f3c6b19a39a98b1999d39526b0d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 14:27:03 +0800 Subject: [PATCH 408/438] Add equals inequality test to ReflectiveDefinitionTest Add a JUnit 5 test method testEquals() to ReflectiveDefinitionTest that calls super.testEquals() and then verifies two ReflectiveDefinitionImpl instances with different names are not equal. Also add required imports (org.junit.jupiter.api.Test, assertNotEquals) and static imports for SINCE and DEPRECATION. --- .../reflect/ReflectiveDefinitionTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java index 19f49318d..f891635a6 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java @@ -18,9 +18,14 @@ package io.microsphere.reflect; +import org.junit.jupiter.api.Test; + import java.util.List; +import static io.microsphere.lang.DeprecationTest.DEPRECATION; +import static io.microsphere.lang.DeprecationTest.SINCE; import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertNotEquals; /** * {@link ReflectiveDefinition} Test @@ -35,4 +40,12 @@ class ReflectiveDefinitionTest extends AbstractReflectiveDefinitionTest getTailConstructorArguments() { return emptyList(); } + + @Test + void testEquals() { + super.testEquals(); + ReflectiveDefinitionImpl df1 = new ReflectiveDefinitionImpl(SINCE, DEPRECATION, "df1"); + ReflectiveDefinitionImpl df2 = new ReflectiveDefinitionImpl(SINCE, DEPRECATION, "df2"); + assertNotEquals(df1, df2); + } } \ No newline at end of file From b38edece092038010c3b869254de5003d0b3a4ae Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 14:41:53 +0800 Subject: [PATCH 409/438] Remove stray blank line in SimpleClassScanner Formatting cleanup in SimpleClassScanner.java: removed an unnecessary blank line between methods and adjusted the file termination (no newline at EOF). No functional changes. --- .../java/io/microsphere/io/scanner/SimpleClassScanner.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleClassScanner.java b/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleClassScanner.java index 8def17b89..b0e512bf7 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleClassScanner.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleClassScanner.java @@ -104,7 +104,6 @@ public Set> scan(ClassLoader classLoader, String packageName, boolean r return scan(classLoader, packageName, recursive, false); } - /** * scan {@link Class} set under specified package name or its' sub-packages in {@link ClassLoader}, if * requiredLoad indicates true , try to load those classes. @@ -194,4 +193,4 @@ private URL resolveClassPathURL(URL resourceURL, String packageResourceName) { String classPath = substringBefore(resource, packageResourceName); return ofURL(classPath); } -} +} \ No newline at end of file From 09143c97fac5c0a4dbe3aa7f7e132b228355864f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 14:42:02 +0800 Subject: [PATCH 410/438] Update SimpleClassScannerTest javadoc & assertions Fix Javadoc references to point to SimpleClassScanner (update @link and @see) and extend the test by performing an additional recursive scan (true) on the nonnullClassResource with an assertion that the result is not empty. Also minor EOF formatting change. --- .../microsphere/io/scanner/SimpleClassScannerTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleClassScannerTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleClassScannerTest.java index 7a7e3d488..ba92570a5 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleClassScannerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/scanner/SimpleClassScannerTest.java @@ -18,10 +18,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** - * {@link SimpleClassScannerTest} + * {@link SimpleClassScanner} Test * * @author Mercy - * @see SimpleClassScannerTest + * @see SimpleClassScanner * @since 1.0.0 */ class SimpleClassScannerTest extends AbstractTestCase { @@ -52,6 +52,8 @@ void testScanInArchive() { URL nonnullClassResource = getClassResource(classLoader, Nonnull.class); Set> classesSet = simpleClassScanner.scan(TEST_CLASS_LOADER, nonnullClassResource, false); assertFalse(classesSet.isEmpty()); - } -} + classesSet = simpleClassScanner.scan(TEST_CLASS_LOADER, nonnullClassResource, true); + assertFalse(classesSet.isEmpty()); + } +} \ No newline at end of file From 1c42ae272488c54b94a858ba0ebdc1ff6bf79fb3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 14:43:30 +0800 Subject: [PATCH 411/438] Make ConstructorUtilsTest extend LoggingTest Import LoggingTest and have ConstructorUtilsTest extend it so the test inherits the shared logging setup/behavior. Adds the io.microsphere.LoggingTest import and changes the test class declaration; no other test logic was modified. --- .../test/java/io/microsphere/reflect/ConstructorUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ConstructorUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ConstructorUtilsTest.java index f84c6b2da..04152166f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ConstructorUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ConstructorUtilsTest.java @@ -1,5 +1,6 @@ package io.microsphere.reflect; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.Test; import static io.microsphere.reflect.ConstructorUtils.findConstructor; @@ -23,7 +24,7 @@ * @see ConstructorUtils * @since 1.0.0 */ -class ConstructorUtilsTest { +class ConstructorUtilsTest extends LoggingTest { @Test void testIsNonPrivateConstructorWithoutParameters() { From bf56cb6bec0e287c3df9ed7fb5348a45c71fc771 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 15:01:09 +0800 Subject: [PATCH 412/438] Add SerializersTest and loadSPI tests Introduce a new JUnit 5 test class io.microsphere.io.SerializersTest to verify Serializers behavior: get() returns empty lists before loadSPI and non-null results after loadSPI; getMostCompatible(), getHighestPriority(), and getLowestPriority() are also tested for String.class and Object.class. Minor non-functional change in Serializers.java (trailing newline adjustment). --- .../java/io/microsphere/io/Serializers.java | 2 +- .../io/microsphere/io/SerializersTest.java | 99 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/io/SerializersTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/Serializers.java b/microsphere-java-core/src/main/java/io/microsphere/io/Serializers.java index c6cd8f36c..63005a21d 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/Serializers.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/Serializers.java @@ -129,4 +129,4 @@ public Serializer getLowestPriority(Class serializedType) { public List> get(Class serializedType) { return (List) typedSerializers.getOrDefault(serializedType, emptyList()); } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/SerializersTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/SerializersTest.java new file mode 100644 index 000000000..61abdcc1c --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/SerializersTest.java @@ -0,0 +1,99 @@ +/* + * 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. + */ + +package io.microsphere.io; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * {@link Serializers} Test + * + * @author Mercy + * @see Serializers + * @since 1.0.0 + */ +class SerializersTest { + + private Serializers serializers; + + @BeforeEach + void setUp() { + this.serializers = new Serializers(); + } + + @Test + void testLoadSPI() { + assertEquals(emptyList(), this.serializers.get(String.class)); + assertEquals(emptyList(), this.serializers.get(Object.class)); + + this.serializers.loadSPI(); + + assertNotNull(this.serializers.get(String.class)); + assertNotNull(this.serializers.get(Object.class)); + } + + @Test + void testGetMostCompatible() { + assertNull(this.serializers.getMostCompatible(String.class)); + assertNull(this.serializers.getMostCompatible(Object.class)); + + this.serializers.loadSPI(); + + assertNotNull(this.serializers.getMostCompatible(String.class)); + assertNotNull(this.serializers.getMostCompatible(Object.class)); + } + + @Test + void testGetHighestPriority() { + assertNull(this.serializers.getHighestPriority(String.class)); + assertNull(this.serializers.getHighestPriority(Object.class)); + + this.serializers.loadSPI(); + + assertNotNull(this.serializers.getHighestPriority(String.class)); + assertNotNull(this.serializers.getHighestPriority(Object.class)); + } + + @Test + void testGetLowestPriority() { + assertNull(this.serializers.getLowestPriority(String.class)); + assertNull(this.serializers.getLowestPriority(Object.class)); + + this.serializers.loadSPI(); + + assertNotNull(this.serializers.getLowestPriority(String.class)); + assertNotNull(this.serializers.getLowestPriority(Object.class)); + } + + @Test + void testGet() { + assertEquals(emptyList(), this.serializers.get(String.class)); + assertEquals(emptyList(), this.serializers.get(Object.class)); + + this.serializers.loadSPI(); + + assertNotNull(this.serializers.getMostCompatible(String.class)); + assertNotNull(this.serializers.getMostCompatible(Object.class)); + } +} \ No newline at end of file From f14e1e0f0a6ec32345693244f0b587b864d404a5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 15:04:28 +0800 Subject: [PATCH 413/438] Add Deserializers unit tests Add JUnit 5 tests for io.microsphere.io.Deserializers (microsphere-java-core/src/test/java/io/microsphere/io/DeserializersTest.java). Tests verify SPI loading and retrieval methods: get, getMostCompatible, getHighestPriority, and getLowestPriority. They assert empty results before loadSPI() and non-null results after loadSPI(), ensuring SPI-based deserializer discovery and priority selection work as expected. --- .../java/io/microsphere/io/Deserializers.java | 2 +- .../io/microsphere/io/DeserializersTest.java | 99 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/io/DeserializersTest.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/Deserializers.java b/microsphere-java-core/src/main/java/io/microsphere/io/Deserializers.java index a81829a02..b9c7aa576 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/Deserializers.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/Deserializers.java @@ -127,4 +127,4 @@ public Deserializer getLowestPriority(Class deserializedType) { public List> get(Class deserializedType) { return (List) typedDeserializers.getOrDefault(deserializedType, emptyList()); } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/DeserializersTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/DeserializersTest.java new file mode 100644 index 000000000..b2ced2e96 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/io/DeserializersTest.java @@ -0,0 +1,99 @@ +/* + * 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. + */ + +package io.microsphere.io; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * {@link Deserializers} Test + * + * @author Mercy + * @see Deserializers + * @since 1.0.0 + */ +class DeserializersTest { + + private Deserializers deserializers; + + @BeforeEach + void setUp() { + this.deserializers = new Deserializers(); + } + + @Test + void testLoadSPI() { + assertEquals(emptyList(), this.deserializers.get(String.class)); + assertEquals(emptyList(), this.deserializers.get(Object.class)); + + this.deserializers.loadSPI(); + + assertNotNull(this.deserializers.get(String.class)); + assertNotNull(this.deserializers.get(Object.class)); + } + + @Test + void testGetMostCompatible() { + assertNull(this.deserializers.getMostCompatible(String.class)); + assertNull(this.deserializers.getMostCompatible(Object.class)); + + this.deserializers.loadSPI(); + + assertNotNull(this.deserializers.getMostCompatible(String.class)); + assertNotNull(this.deserializers.getMostCompatible(Object.class)); + } + + @Test + void testGetHighestPriority() { + assertNull(this.deserializers.getHighestPriority(String.class)); + assertNull(this.deserializers.getHighestPriority(Object.class)); + + this.deserializers.loadSPI(); + + assertNotNull(this.deserializers.getHighestPriority(String.class)); + assertNotNull(this.deserializers.getHighestPriority(Object.class)); + } + + @Test + void testGetLowestPriority() { + assertNull(this.deserializers.getLowestPriority(String.class)); + assertNull(this.deserializers.getLowestPriority(Object.class)); + + this.deserializers.loadSPI(); + + assertNotNull(this.deserializers.getLowestPriority(String.class)); + assertNotNull(this.deserializers.getLowestPriority(Object.class)); + } + + @Test + void testGet() { + assertEquals(emptyList(), this.deserializers.get(String.class)); + assertEquals(emptyList(), this.deserializers.get(Object.class)); + + this.deserializers.loadSPI(); + + assertNotNull(this.deserializers.getMostCompatible(String.class)); + assertNotNull(this.deserializers.getMostCompatible(Object.class)); + } +} \ No newline at end of file From feed2d93ee6ec51af78875b4d195968e54d6bd52 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 15:11:43 +0800 Subject: [PATCH 414/438] Simplify PropertyResourceBundleControl IO handling Remove redundant null-check around URLConnection and always disable caches/get input stream when a URL is found. Consolidate reader creation and resource cleanup into a single try-finally to simplify code and centralize stream/reader closing. --- .../util/PropertyResourceBundleControl.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/PropertyResourceBundleControl.java b/microsphere-java-core/src/main/java/io/microsphere/util/PropertyResourceBundleControl.java index 9fbeeb7d5..95c34be83 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/PropertyResourceBundleControl.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/PropertyResourceBundleControl.java @@ -109,12 +109,9 @@ public ResourceBundle newBundle(String baseName, Locale locale, String format, f URL url = classLoader.getResource(resourceName); if (url != null) { URLConnection connection = url.openConnection(); - if (connection != null) { - // Disable caches to get fresh data for - // reloading. - connection.setUseCaches(false); - is = connection.getInputStream(); - } + // Disable caches to get fresh data for reloading. + connection.setUseCaches(false); + is = connection.getInputStream(); } } else { is = classLoader.getResourceAsStream(resourceName); @@ -129,14 +126,12 @@ public ResourceBundle newBundle(String baseName, Locale locale, String format, f throw (IOException) e.getException(); } - if (stream != null) { - try { - reader = new InputStreamReader(stream, this.getEncoding()); - bundle = new PropertyResourceBundle(reader); - } finally { - close(stream); - close(reader); - } + try { + reader = new InputStreamReader(stream, this.getEncoding()); + bundle = new PropertyResourceBundle(reader); + } finally { + close(stream); + close(reader); } return bundle; From 56bbce0ed39b719095d62bf6970a2a12626dc62f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 15:30:20 +0800 Subject: [PATCH 415/438] Add Supplier-based resolveClassPaths helper Consolidate classpath resolution logic by introducing resolveClassPaths(boolean, Supplier) and delegating initBootstrapClassPaths/initClassPaths to it. This removes duplication around checking whether a classpath is supported and fetching it via RuntimeMXBean. Also adds unit test coverage (testResolveClassPaths) to assert emptySet behavior for unsupported or empty classpath suppliers and updates imports accordingly. --- .../java/io/microsphere/util/ClassPathUtils.java | 15 ++++++++++----- .../io/microsphere/util/ClassPathUtilsTest.java | 9 +++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java index a3a1c8784..9fe35817b 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java @@ -11,6 +11,7 @@ import java.lang.management.RuntimeMXBean; import java.net.URL; import java.util.Set; +import java.util.function.Supplier; import static io.microsphere.collection.SetUtils.ofSet; import static io.microsphere.constants.SeparatorConstants.PATH_SEPARATOR; @@ -66,16 +67,20 @@ public abstract class ClassPathUtils implements Utils { @Nonnull @Immutable private static Set initBootstrapClassPaths() { - if (runtimeMXBean.isBootClassPathSupported()) { - return resolveClassPaths(runtimeMXBean.getBootClassPath()); - } - return emptySet(); + return resolveClassPaths(runtimeMXBean.isBootClassPathSupported(), runtimeMXBean::getBootClassPath); } @Nonnull @Immutable private static Set initClassPaths() { - return resolveClassPaths(runtimeMXBean.getClassPath()); + return resolveClassPaths(true, runtimeMXBean::getClassPath); + } + + static Set resolveClassPaths(boolean classPathSupported, Supplier classPathSupplier) { + if (classPathSupported) { + return resolveClassPaths(classPathSupplier.get()); + } + return emptySet(); } @Nonnull diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java index bcbc1b748..57fce58ce 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassPathUtilsTest.java @@ -17,11 +17,14 @@ import static io.microsphere.util.ClassPathUtils.getBootstrapClassPaths; import static io.microsphere.util.ClassPathUtils.getClassPaths; import static io.microsphere.util.ClassPathUtils.getRuntimeClassLocation; +import static io.microsphere.util.ClassPathUtils.resolveClassPaths; import static java.lang.management.ManagementFactory.getRuntimeMXBean; +import static java.util.Collections.emptySet; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link ClassPathUtils} {@link Test} @@ -90,4 +93,10 @@ void testGetRuntimeClassLocationWithClassName() { assertNotNull(location); log(location); } + + @Test + void testResolveClassPaths() { + assertSame(emptySet(), resolveClassPaths(false, () -> null)); + assertSame(emptySet(), resolveClassPaths(true, () -> "")); + } } From 22c4db0d14f3b947e34b27e505c618ede0083d01 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 15:36:29 +0800 Subject: [PATCH 416/438] Extract createArtifact and add tests Refactor ArchiveFileArtifactResourceResolver by extracting the filename-to-artifact parsing into a new static createArtifact(String, URL) method. resolve(...) now delegates to createArtifact(...). Added unit test testCreateArtifact to validate parsing of names like 'a-1.0.jar', 'a.jar', and empty input (null). This improves reuse and testability of artifact creation logic. --- .../ArchiveFileArtifactResourceResolver.java | 7 ++++--- ...rchiveFileArtifactResourceResolverTest.java | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/classloading/ArchiveFileArtifactResourceResolver.java b/microsphere-java-core/src/main/java/io/microsphere/classloading/ArchiveFileArtifactResourceResolver.java index 3c79ab372..ffb9b4369 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/classloading/ArchiveFileArtifactResourceResolver.java +++ b/microsphere-java-core/src/main/java/io/microsphere/classloading/ArchiveFileArtifactResourceResolver.java @@ -88,8 +88,10 @@ public Artifact resolve(URL resourceURL) { return null; } - String archiveFileName = archiveFile.getName(); + return createArtifact(archiveFile.getName(), resourceURL); + } + static Artifact createArtifact(String archiveFileName, URL resourceURL) { String fileNameWithoutExtension = substringBeforeLast(archiveFileName, DOT); String[] parts = split(fileNameWithoutExtension, HYPHEN); @@ -104,5 +106,4 @@ public Artifact resolve(URL resourceURL) { String version = length > 1 ? parts[1] : null; return create(artifactId, version, resourceURL); } - -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/classloading/ArchiveFileArtifactResourceResolverTest.java b/microsphere-java-core/src/test/java/io/microsphere/classloading/ArchiveFileArtifactResourceResolverTest.java index 3a8005a3a..28f70c4bc 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/classloading/ArchiveFileArtifactResourceResolverTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/classloading/ArchiveFileArtifactResourceResolverTest.java @@ -16,10 +16,13 @@ */ package io.microsphere.classloading; +import org.junit.jupiter.api.Test; + import java.io.File; import java.net.MalformedURLException; import java.net.URL; +import static io.microsphere.classloading.ArchiveFileArtifactResourceResolver.createArtifact; import static io.microsphere.net.URLUtils.ofURL; import static io.microsphere.util.ClassLoaderUtils.getClassResource; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -44,6 +47,21 @@ void testResolve(ArchiveFileArtifactResourceResolver resolver) throws Throwable testResolveOnArchiveFile(resolver); } + @Test + void testCreateArtifact() { + Artifact artifact = createArtifact("a-1.0.jar", null); + assertEquals("a", artifact.getArtifactId()); + assertEquals("1.0", artifact.getVersion()); + assertNull(artifact.getLocation()); + + artifact = createArtifact("a.jar", null); + assertEquals("a", artifact.getArtifactId()); + assertNull(artifact.getVersion()); + assertNull(artifact.getLocation()); + + assertNull(createArtifact("", null)); + } + void testResolveOnNull(ArchiveFileArtifactResourceResolver resolver) { assertNull(resolver.resolve(null)); } From e5246bddae46632acf590e042a6acf350cae4851 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 15:47:45 +0800 Subject: [PATCH 417/438] Extract assertCapacity and add corresponding test Refactor FastByteArrayOutputStream to extract the array-size check into a package-private assertCapacity(int) method and call it from ensureCapacity. Also relax MAX_ARRAY_SIZE visibility to package-private. Update unit tests to use explicit this. and add a test asserting assertCapacity(Integer.MAX_VALUE) throws OutOfMemoryError to increase coverage of the overflow check. --- .../io/FastByteArrayOutputStream.java | 10 ++-- .../io/FastByteArrayOutputStreamTest.java | 51 ++++++++++--------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayOutputStream.java b/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayOutputStream.java index 6805d5581..f04bcff56 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayOutputStream.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/FastByteArrayOutputStream.java @@ -60,7 +60,7 @@ public class FastByteArrayOutputStream extends ByteArrayOutputStream { * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ - private static final int MAX_ARRAY_SIZE = MAX_VALUE - 8; + static final int MAX_ARRAY_SIZE = MAX_VALUE - 8; /** * {@inheritDoc} @@ -107,10 +107,14 @@ private void grow(int minCapacity) { int newCapacity = oldCapacity << 1; if (newCapacity - minCapacity < 0) newCapacity = minCapacity; + assertCapacity(newCapacity); + buf = copyOf(buf, newCapacity); + } + + void assertCapacity(int newCapacity) { if (newCapacity - MAX_ARRAY_SIZE > 0) { throw new OutOfMemoryError(); } - buf = copyOf(buf, newCapacity); } /** @@ -183,4 +187,4 @@ public String toString(String charsetName) */ public void close() { } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/io/FastByteArrayOutputStreamTest.java b/microsphere-java-core/src/test/java/io/microsphere/io/FastByteArrayOutputStreamTest.java index 4dab95a88..75cd7a797 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/io/FastByteArrayOutputStreamTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/io/FastByteArrayOutputStreamTest.java @@ -27,12 +27,12 @@ class FastByteArrayOutputStreamTest { @BeforeEach void setUp() { - outputStream = new FastByteArrayOutputStream(2); + this.outputStream = new FastByteArrayOutputStream(2); } @AfterEach void tearDown() { - outputStream.close(); + this.outputStream.close(); } @Test @@ -43,44 +43,44 @@ void testConstructorOnIllegalArgumentException() { @Test void testWrite() { for (byte b : TEST_BYTES) { - outputStream.write(b); + this.outputStream.write(b); } - assertArrayEquals(outputStream.toByteArray(), TEST_BYTES); + assertArrayEquals(this.outputStream.toByteArray(), TEST_BYTES); } @Test void testWrite0() throws IOException { - outputStream.write(TEST_BYTES); - assertArrayEquals(outputStream.toByteArray(), TEST_BYTES); + this.outputStream.write(TEST_BYTES); + assertArrayEquals(this.outputStream.toByteArray(), TEST_BYTES); } @Test void testWrite0OnNullPointerException() { - assertThrows(NullPointerException.class, () -> outputStream.write(null)); + assertThrows(NullPointerException.class, () -> this.outputStream.write(null)); } @Test void testWrite0OnIndexOutOfBoundsException() { - assertThrows(IndexOutOfBoundsException.class, () -> outputStream.write(TEST_BYTES, -1, 0)); - assertThrows(IndexOutOfBoundsException.class, () -> outputStream.write(TEST_BYTES, TEST_BYTES.length + 1, 0)); - assertThrows(IndexOutOfBoundsException.class, () -> outputStream.write(TEST_BYTES, 0, -1)); - assertThrows(IndexOutOfBoundsException.class, () -> outputStream.write(TEST_BYTES, 0, TEST_BYTES.length + 1)); + assertThrows(IndexOutOfBoundsException.class, () -> this.outputStream.write(TEST_BYTES, -1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> this.outputStream.write(TEST_BYTES, TEST_BYTES.length + 1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> this.outputStream.write(TEST_BYTES, 0, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> this.outputStream.write(TEST_BYTES, 0, TEST_BYTES.length + 1)); } @Test void testWriteTo() throws IOException { FastByteArrayOutputStream toOutputStream = new FastByteArrayOutputStream(); - outputStream.write(TEST_BYTES); - outputStream.writeTo(toOutputStream); - assertArrayEquals(outputStream.toByteArray(), toOutputStream.toByteArray()); + this.outputStream.write(TEST_BYTES); + this.outputStream.writeTo(toOutputStream); + assertArrayEquals(this.outputStream.toByteArray(), toOutputStream.toByteArray()); } @Test void testReset() throws IOException { testSize(); - outputStream.reset(); - assertEquals(0, outputStream.size()); + this.outputStream.reset(); + assertEquals(0, this.outputStream.size()); } @Test @@ -90,25 +90,30 @@ void testToByteArray() throws IOException { @Test void testSize() throws IOException { - assertEquals(0, outputStream.size()); - outputStream.write(TEST_BYTES); - assertEquals(TEST_BYTES.length, outputStream.size()); + assertEquals(0, this.outputStream.size()); + this.outputStream.write(TEST_BYTES); + assertEquals(TEST_BYTES.length, this.outputStream.size()); } @Test void testToString() throws IOException { - outputStream.write(TEST_BYTES); - assertEquals(TEST_VALUE, outputStream.toString()); + this.outputStream.write(TEST_BYTES); + assertEquals(TEST_VALUE, this.outputStream.toString()); } @Test void testToString1() throws IOException { - outputStream.write(TEST_BYTES); - assertEquals(TEST_VALUE, outputStream.toString("UTF-8")); + this.outputStream.write(TEST_BYTES); + assertEquals(TEST_VALUE, this.outputStream.toString("UTF-8")); } @Test void testClose() { tearDown(); } + + @Test + void testAssertCapacityOnOutOfMemoryError() { + assertThrows(OutOfMemoryError.class, () -> this.outputStream.assertCapacity(Integer.MAX_VALUE)); + } } \ No newline at end of file From 43a7da69c1ef2e3bf02b17a08172666835eef011 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 16:12:54 +0800 Subject: [PATCH 418/438] Make selector package-private; extract test factories Changed CompositeSubProtocolURLConnectionFactory.selectFactory and selectFactoryIndex from private to package-private to allow test access. Updated CompositeSubProtocolURLConnectionFactoryTest to use a @BeforeEach setup, use NO_PROXY and ofList helpers, and added new tests (testRemove, testSupports). Removed inline test factory classes from ExtendableProtocolURLStreamHandlerTest and added them as separate test files (FalseSubProtocolURLConnectionFactory, NullSubProtocolURLConnectionFactory). Adjusted imports accordingly to improve testability and reuse of test helpers. --- ...positeSubProtocolURLConnectionFactory.java | 6 +-- ...teSubProtocolURLConnectionFactoryTest.java | 44 +++++++++++++++++-- ...xtendableProtocolURLStreamHandlerTest.java | 27 ------------ .../FalseSubProtocolURLConnectionFactory.java | 44 +++++++++++++++++++ .../NullSubProtocolURLConnectionFactory.java | 44 +++++++++++++++++++ 5 files changed, 131 insertions(+), 34 deletions(-) create mode 100644 microsphere-java-core/src/test/java/io/microsphere/net/FalseSubProtocolURLConnectionFactory.java create mode 100644 microsphere-java-core/src/test/java/io/microsphere/net/NullSubProtocolURLConnectionFactory.java diff --git a/microsphere-java-core/src/main/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactory.java b/microsphere-java-core/src/main/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactory.java index e8baf1ad7..b9fe07d96 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactory.java @@ -131,12 +131,12 @@ public URLConnection create(URL url, List subProtocols, Proxy proxy) thr return factory.create(url, subProtocols, proxy); } - private SubProtocolURLConnectionFactory selectFactory(URL url, List subProtocols) { + SubProtocolURLConnectionFactory selectFactory(URL url, List subProtocols) { int index = selectFactoryIndex(url, subProtocols); return factories.get(index); } - private int selectFactoryIndex(URL url, List subProtocols) { + int selectFactoryIndex(URL url, List subProtocols) { int index = -1; int size = factories.size(); for (int i = 0; i < size; i++) { @@ -148,4 +148,4 @@ private int selectFactoryIndex(URL url, List subProtocols) { } return index; } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactoryTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactoryTest.java index 850648615..57ae3eaf8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactoryTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/CompositeSubProtocolURLConnectionFactoryTest.java @@ -17,17 +17,21 @@ package io.microsphere.net; import io.microsphere.net.console.ConsoleURLConnection; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; -import java.net.Proxy; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.List; +import static io.microsphere.collection.Lists.ofList; +import static java.net.Proxy.NO_PROXY; import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link CompositeSubProtocolURLConnectionFactory} Test @@ -37,16 +41,23 @@ */ class CompositeSubProtocolURLConnectionFactoryTest { + private CompositeSubProtocolURLConnectionFactory composite; + + @BeforeEach + void setUp() { + this.composite = new CompositeSubProtocolURLConnectionFactory(); + } + @Test void test() throws IOException { - CompositeSubProtocolURLConnectionFactory composite = new CompositeSubProtocolURLConnectionFactory(); + CompositeSubProtocolURLConnectionFactory composite = this.composite; ConsoleSubProtocolURLConnectionFactory instance = new ConsoleSubProtocolURLConnectionFactory(); composite.add(instance); URL url = new URL("ftp://..."); List subProtocols = emptyList(); if (composite.supports(url, subProtocols)) { - URLConnection urlConnection = composite.create(url, subProtocols, Proxy.NO_PROXY); + URLConnection urlConnection = composite.create(url, subProtocols, NO_PROXY); assertEquals(ConsoleURLConnection.class, urlConnection.getClass()); } @@ -57,4 +68,29 @@ void test() throws IOException { composite.remove(instance); assertFalse(composite.supports(url, subProtocols)); } -} + + @Test + void testRemove() { + CompositeSubProtocolURLConnectionFactory composite = this.composite; + assertFalse(composite.remove(null)); + assertFalse(composite.remove(composite)); + + FalseSubProtocolURLConnectionFactory factory = new FalseSubProtocolURLConnectionFactory(); + composite.addInternal(factory); + assertTrue(composite.remove(factory)); + } + + @Test + void testSupports() throws MalformedURLException { + CompositeSubProtocolURLConnectionFactory composite = this.composite; + + URL url = new URL("ftp://..."); + + assertFalse(composite.supports(url, ofList("test"))); + + FalseSubProtocolURLConnectionFactory factory = new FalseSubProtocolURLConnectionFactory(); + + composite.addInternal(factory); + assertFalse(composite.supports(url, ofList("test"))); + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/ExtendableProtocolURLStreamHandlerTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/ExtendableProtocolURLStreamHandlerTest.java index 634942027..372b73328 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/ExtendableProtocolURLStreamHandlerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/ExtendableProtocolURLStreamHandlerTest.java @@ -23,9 +23,7 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.Proxy; import java.net.URL; -import java.net.URLConnection; import java.util.List; import static io.microsphere.collection.ListUtils.newLinkedList; @@ -266,29 +264,4 @@ void testReformSpec() throws MalformedURLException { private static class MemberClass extends ExtendableProtocolURLStreamHandler { } - static class FalseSubProtocolURLConnectionFactory implements SubProtocolURLConnectionFactory { - - @Override - public boolean supports(URL url, List subProtocols) { - return false; - } - - @Override - public URLConnection create(URL url, List subProtocols, Proxy proxy) throws IOException { - return null; - } - } - - static class NullSubProtocolURLConnectionFactory implements SubProtocolURLConnectionFactory { - - @Override - public boolean supports(URL url, List subProtocols) { - return true; - } - - @Override - public URLConnection create(URL url, List subProtocols, Proxy proxy) throws IOException { - return null; - } - } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/FalseSubProtocolURLConnectionFactory.java b/microsphere-java-core/src/test/java/io/microsphere/net/FalseSubProtocolURLConnectionFactory.java new file mode 100644 index 000000000..aa0777789 --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/net/FalseSubProtocolURLConnectionFactory.java @@ -0,0 +1,44 @@ +/* + * 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. + */ + +package io.microsphere.net; + +import java.io.IOException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.List; + +/** + * {@link SubProtocolURLConnectionFactory} returns {@code false} for {@link #supports(URL, List)} + * + * @author Mercy + * @see SubProtocolURLConnectionFactory + * @since 1.0.0 + */ +class FalseSubProtocolURLConnectionFactory implements SubProtocolURLConnectionFactory { + + @Override + public boolean supports(URL url, List subProtocols) { + return false; + } + + @Override + public URLConnection create(URL url, List subProtocols, Proxy proxy) throws IOException { + return null; + } +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/NullSubProtocolURLConnectionFactory.java b/microsphere-java-core/src/test/java/io/microsphere/net/NullSubProtocolURLConnectionFactory.java new file mode 100644 index 000000000..e91f36a3a --- /dev/null +++ b/microsphere-java-core/src/test/java/io/microsphere/net/NullSubProtocolURLConnectionFactory.java @@ -0,0 +1,44 @@ +/* + * 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. + */ + +package io.microsphere.net; + +import java.io.IOException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.List; + +/** + * Null {@link SubProtocolURLConnectionFactory} + * + * @author Mercy + * @see SubProtocolURLConnectionFactory + * @since 1.0.0 + */ +class NullSubProtocolURLConnectionFactory implements SubProtocolURLConnectionFactory { + + @Override + public boolean supports(URL url, List subProtocols) { + return true; + } + + @Override + public URLConnection create(URL url, List subProtocols, Proxy proxy) throws IOException { + return null; + } +} From 5702ae0f6e9822466057b2a683094cbfb6a860ec Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 16:19:43 +0800 Subject: [PATCH 419/438] Use instanceof in ValueHolder.equals Replace strict getClass() check with instanceof in ValueHolder.equals so ValueHolder subclasses can be considered equal; add braces/formatting for clarity. Update ValueHolderTest to reflect the behavior (add static import for Integer.valueOf, reorder assertions, and add an assertion ensuring a ValueHolder is not equal to a raw Integer). --- .../src/main/java/io/microsphere/util/ValueHolder.java | 10 +++++++--- .../test/java/io/microsphere/util/ValueHolderTest.java | 6 ++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ValueHolder.java b/microsphere-java-core/src/main/java/io/microsphere/util/ValueHolder.java index 6518f8a15..a4e9d616c 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ValueHolder.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ValueHolder.java @@ -69,8 +69,12 @@ public void reset() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (!(o instanceof ValueHolder)) { + return false; + } ValueHolder that = (ValueHolder) o; return Objects.equals(value, that.value); } @@ -91,4 +95,4 @@ public static ValueHolder of(V value) { return new ValueHolder<>(value); } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ValueHolderTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ValueHolderTest.java index 3a8ac942d..292250ba9 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ValueHolderTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ValueHolderTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import static io.microsphere.util.ValueHolder.of; +import static java.lang.Integer.valueOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -80,10 +81,11 @@ void testToString() { void testEquals() { ValueHolder valueHolder = of(1); assertEquals(valueHolder, valueHolder); - assertNotEquals(1, valueHolder); - assertEquals(of(1), valueHolder); assertEquals(of("A"), of("A")); + assertEquals(of(1), valueHolder); assertEquals(of(null), of(null)); + assertNotEquals(1, valueHolder); + assertNotEquals(valueHolder, valueOf(1)); } @Test From 9757026f1f4c49d163fc9e9a217a54988ba35971 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 16:48:41 +0800 Subject: [PATCH 420/438] Refactor MethodKey equals/hashCode and update tests Refactor MethodUtils and its tests: adjust the trace log formatting, change MethodKey constructor to varargs (Class...), and fix equals to use instanceof with clearer field comparisons. Simplify hashCode to use existing hash(...) helpers and normalize toString usage. Update MethodUtilsTest to extend LoggingTest, import MethodKey, add null/inequality checks and a new test for nearest overridden method on Object, and add assertions validating MethodKey equality/inequality and behavior. --- .../io/microsphere/reflect/MethodUtils.java | 30 ++++++++++--------- .../microsphere/reflect/MethodUtilsTest.java | 30 +++++++++++++++++-- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java index 8e5d47c1e..7fbed49b2 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java @@ -1360,9 +1360,8 @@ static Method doFindMethod(Method[] methods, String methodName, Class[] param } } if (logger.isTraceEnabled()) { - logger.trace("The target method[name : '{}' , parameter types : {}] {} found in the methods : {}", - methodName, arrayToString(parameterTypes), targetMethod == null ? "can't be" : "is", - arrayToString(methods)); + logger.trace("To find the target method[name : '{}' , parameter types : {} , methods : {}] : {}", + methodName, arrayToString(parameterTypes), arrayToString(methods), targetMethod); } return targetMethod; } @@ -1410,7 +1409,7 @@ static class MethodKey { final Class[] parameterTypes; - MethodKey(Class declaredClass, String methodName, Class[] parameterTypes) { + MethodKey(Class declaredClass, String methodName, Class... parameterTypes) { this.declaredClass = declaredClass; this.methodName = methodName; this.parameterTypes = parameterTypes == null ? EMPTY_CLASS_ARRAY : parameterTypes; @@ -1418,28 +1417,31 @@ static class MethodKey { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (!(o instanceof MethodKey)) { + return false; + } MethodKey methodKey = (MethodKey) o; - if (!Objects.equals(declaredClass, methodKey.declaredClass)) return false; - if (!Objects.equals(methodName, methodKey.methodName)) return false; - // Probably incorrect - comparing Object[] arrays with Arrays.equals - return arrayEquals(parameterTypes, methodKey.parameterTypes); + return Objects.equals(this.declaredClass, methodKey.declaredClass) + && Objects.equals(this.methodName, methodKey.methodName) + && arrayEquals(this.parameterTypes, methodKey.parameterTypes); } @Override public int hashCode() { - int result = declaredClass != null ? declaredClass.hashCode() : 0; - result = 31 * result + (methodName != null ? methodName.hashCode() : 0); - result = 31 * result + hash(parameterTypes); + int result = hash(this.declaredClass); + result = 31 * result + hash(this.methodName); + result = 31 * result + hash(this.parameterTypes); return result; } @Override public String toString() { - return buildSignature(declaredClass, methodName, parameterTypes); + return buildSignature(this.declaredClass, this.methodName, this.parameterTypes); } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java index 1d5cc6ff3..4450f330d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java @@ -16,7 +16,9 @@ */ package io.microsphere.reflect; +import io.microsphere.LoggingTest; import io.microsphere.lang.Prioritized; +import io.microsphere.reflect.MethodUtils.MethodKey; import io.microsphere.test.Data; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; @@ -28,6 +30,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.function.Predicate; import static io.microsphere.AbstractTestCase.JACOCO_AGENT_INSTRUCTED; @@ -80,6 +83,7 @@ import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; @@ -92,7 +96,7 @@ * @author Mercy * @since 1.0.0 */ -class MethodUtilsTest { +class MethodUtilsTest extends LoggingTest { private static final int JACOCO_ADDED_METHOD_COUNT; @@ -495,6 +499,9 @@ void testOverridesOnStaticMethod() { Method valueOfMethod2 = findMethod(String.class, "valueOf", int.class); assertFalse(overrides(valueOfMethod1, valueOfMethod2)); assertFalse(overrides(valueOfMethod2, valueOfMethod1)); + + assertFalse(overrides(findMethod(Object.class, "equals", Object.class), + findMethod(Objects.class, "equals", Object.class, Object.class))); } @Test @@ -574,6 +581,12 @@ void testFindNearestOverriddenMethod() { assertEquals(overridden, findNearestOverriddenMethod(overrider)); } + @Test + void testFindNearestOverriddenMethodOnObject() { + Method overrider = findMethod(Object.class, "toString"); + assertNull(findNearestOverriddenMethod(overrider)); + } + @Test void test() { Method[] methods = TestSubInterface.class.getDeclaredMethods(); @@ -738,12 +751,23 @@ void testBuildKey() { assertMethodKey(String.class, "toString"); assertMethodKey(ReflectionTest.class, "publicMethod", int.class); assertMethodKey(Appendable.class, "append", CharSequence.class, int.class, int.class); + + + MethodKey key1 = new MethodKey(String.class, "toString"); + MethodKey key2 = new MethodKey(String.class, "equals", Object.class); + MethodKey key3 = new MethodKey(Object.class, "equals", Object.class); + + + assertNotEquals(key1, key2); + assertNotEquals(key1, key3); + assertNotEquals(key1, "String"); } private void assertMethodKey(Class declaredClass, String methodName, Class... parameterTypes) { - MethodUtils.MethodKey methodKey1 = buildKey(declaredClass, methodName, parameterTypes); - MethodUtils.MethodKey methodKey2 = buildKey(declaredClass, methodName, parameterTypes.length == 0 ? null : parameterTypes); + MethodKey methodKey1 = buildKey(declaredClass, methodName, parameterTypes); + MethodKey methodKey2 = buildKey(declaredClass, methodName, parameterTypes.length == 0 ? null : parameterTypes); + assertEquals(methodKey1, methodKey1); assertEquals(methodKey1, methodKey2); assertEquals(methodKey1.hashCode(), methodKey2.hashCode()); assertEquals(methodKey1.toString(), methodKey2.toString()); From 6ab3963421f549ff847af8006e53c71efe279cfd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 17:00:36 +0800 Subject: [PATCH 421/438] Refactor resource lookup in SimpleClassScanner Rework resource discovery and error handling when scanning a package: remove the surrounding try/catch and use ThrowableSupplier.execute to wrap checked IO operations (getResources and new File(...).toURI().toURL()). Use ListUtils.newArrayList for repository results and simplify control flow by separating resourceURL fallback (ClassDataRepository) from class name resolution and class loading loops. Clean up unused imports (IOException, ArrayList) and keep behavior of collecting and loading classes intact. --- .../io/scanner/SimpleClassScanner.java | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleClassScanner.java b/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleClassScanner.java index b0e512bf7..bebf2582b 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleClassScanner.java +++ b/microsphere-java-core/src/main/java/io/microsphere/io/scanner/SimpleClassScanner.java @@ -9,17 +9,17 @@ import io.microsphere.lang.ClassDataRepository; import java.io.File; -import java.io.IOException; import java.net.URL; -import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; +import static io.microsphere.collection.ListUtils.newArrayList; import static io.microsphere.filter.FilterUtils.filter; import static io.microsphere.lang.function.Streams.filterAll; +import static io.microsphere.lang.function.ThrowableSupplier.execute; import static io.microsphere.net.URLUtils.ofURL; import static io.microsphere.net.URLUtils.resolveArchiveFile; import static io.microsphere.util.ClassLoaderUtils.ResourceType.PACKAGE; @@ -123,41 +123,37 @@ public Set> scan(ClassLoader classLoader, String packageName, final boo final String packageResourceName = PACKAGE.resolve(packageName); - try { - Set classNames = new LinkedHashSet(); - // Find in class loader - Set resourceURLs = getResources(classLoader, PACKAGE, packageName); - - if (resourceURLs.isEmpty()) { - //Find in class path - ClassDataRepository repository = ClassDataRepository.INSTANCE; - List classNamesInPackage = new ArrayList<>(repository.getClassNamesInPackage(packageName)); - - if (!classNamesInPackage.isEmpty()) { - String classPath = repository.findClassPath(classNamesInPackage.get(0)); - URL resourceURL = new File(classPath).toURI().toURL(); - resourceURLs = new HashSet(); - resourceURLs.add(resourceURL); - } - } + Set classNames = new LinkedHashSet(); + // Find in class loader + Set resourceURLs = execute(() -> getResources(classLoader, PACKAGE, packageName)); - for (URL resourceURL : resourceURLs) { - URL classPathURL = resolveClassPathURL(resourceURL, packageResourceName); - String classPath = classPathURL.getFile(); - Set classNamesInClassPath = findClassNamesInClassPath(classPath, true); - classNames.addAll(filterClassNames(classNamesInClassPath, packageName, recursive)); - } + if (resourceURLs.isEmpty()) { + //Find in class path + ClassDataRepository repository = ClassDataRepository.INSTANCE; + List classNamesInPackage = newArrayList(repository.getClassNamesInPackage(packageName)); - for (String className : classNames) { - Class class_ = requiredLoad ? loadClass(classLoader, className) : findLoadedClass(classLoader, className); - if (class_ != null) { - classesSet.add(class_); - } + if (!classNamesInPackage.isEmpty()) { + String classPath = repository.findClassPath(classNamesInPackage.get(0)); + URL resourceURL = execute(() -> new File(classPath).toURI().toURL()); + resourceURLs = new HashSet(); + resourceURLs.add(resourceURL); } + } - } catch (IOException e) { + for (URL resourceURL : resourceURLs) { + URL classPathURL = resolveClassPathURL(resourceURL, packageResourceName); + String classPath = classPathURL.getFile(); + Set classNamesInClassPath = findClassNamesInClassPath(classPath, true); + classNames.addAll(filterClassNames(classNamesInClassPath, packageName, recursive)); + } + for (String className : classNames) { + Class class_ = requiredLoad ? loadClass(classLoader, className) : findLoadedClass(classLoader, className); + if (class_ != null) { + classesSet.add(class_); + } } + return unmodifiableSet(classesSet); } From ea79d8977c7c5557e279d808c9ef2fd239f0d2a2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 17:10:40 +0800 Subject: [PATCH 422/438] Simplify MethodUtils exception handling and tests Remove the unused 'accessible' flag and simplify exception handling in MethodUtils.invoke (call trySetAccessible without tracking and merge IllegalAccessException and IllegalArgumentException into a single IllegalArgumentException with an argument-mismatch message). Update tests: import isCallerSensitiveMethod, ensure Object.class methods are excluded in the predicate test, and add a test that verifies no methods on MethodUtilsTest are treated as caller-sensitive. --- .../java/io/microsphere/reflect/MethodUtils.java | 8 ++------ .../java/io/microsphere/reflect/MethodUtilsTest.java | 12 ++++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java index 7fbed49b2..c7f38d9af 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/reflect/MethodUtils.java @@ -945,15 +945,11 @@ public static R invokeMethod(@Nullable Object instance, Method method, Objec throw new NullPointerException("The 'method' must not be null"); } R result = null; - boolean accessible = false; RuntimeException failure = null; try { - accessible = trySetAccessible(method); + trySetAccessible(method); result = (R) method.invoke(instance, arguments); - } catch (IllegalAccessException e) { - String errorMessage = format("The method[signature : '{}' , instance : {}] can't be accessed[accessible : {}]", getSignature(method), instance, accessible); - failure = new IllegalStateException(errorMessage, e); - } catch (IllegalArgumentException e) { + } catch (IllegalAccessException | IllegalArgumentException e) { String errorMessage = format("The arguments can't match the method[signature : '{}' , instance : {}] : {}", getSignature(method), instance, arrayToString(arguments)); failure = new IllegalArgumentException(errorMessage, e); } catch (InvocationTargetException e) { diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java index 4450f330d..62e8a4345 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java @@ -69,6 +69,7 @@ import static io.microsphere.reflect.MethodUtils.initBannedMethods; import static io.microsphere.reflect.MethodUtils.invokeMethod; import static io.microsphere.reflect.MethodUtils.invokeStaticMethod; +import static io.microsphere.reflect.MethodUtils.isCallerSensitiveMethod; import static io.microsphere.reflect.MethodUtils.isGetterMethod; import static io.microsphere.reflect.MethodUtils.isIsMethod; import static io.microsphere.reflect.MethodUtils.isObjectMethod; @@ -194,6 +195,9 @@ void testExcludedDeclaredClass() { for (Method method : ReflectionTest.class.getDeclaredMethods()) { assertTrue(predicate.test(method)); } + for (Method method : Object.class.getDeclaredMethods()) { + assertFalse(predicate.test(method)); + } } @Test @@ -763,6 +767,14 @@ void testBuildKey() { assertNotEquals(key1, "String"); } + @Test + void testIsCallerSensitiveMethod() { + Method[] methods = MethodUtilsTest.class.getMethods(); + for (Method method : methods) { + assertFalse(isCallerSensitiveMethod(method)); + } + } + private void assertMethodKey(Class declaredClass, String methodName, Class... parameterTypes) { MethodKey methodKey1 = buildKey(declaredClass, methodName, parameterTypes); MethodKey methodKey2 = buildKey(declaredClass, methodName, parameterTypes.length == 0 ? null : parameterTypes); From 0b47749503099bd9d90a7e9c5d4fcd84f7e30ab3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 17:14:21 +0800 Subject: [PATCH 423/438] Extend test with LoggingTest Import io.microsphere.LoggingTest and have MethodHandlesLookupUtilsTest extend LoggingTest to enable logging utilities/support during test execution. --- .../io/microsphere/invoke/MethodHandlesLookupUtilsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/invoke/MethodHandlesLookupUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/invoke/MethodHandlesLookupUtilsTest.java index d719a4039..87a716a56 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/invoke/MethodHandlesLookupUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/invoke/MethodHandlesLookupUtilsTest.java @@ -16,6 +16,7 @@ */ package io.microsphere.invoke; +import io.microsphere.LoggingTest; import org.junit.jupiter.api.Test; import java.lang.invoke.MethodHandle; @@ -33,7 +34,7 @@ * @author Mercy * @since 1.0.0 */ -class MethodHandlesLookupUtilsTest { +class MethodHandlesLookupUtilsTest extends LoggingTest { @Test void testFindPublicVirtual() throws Throwable { From e25a6b9590260638c54cac4719fd27d4dfe0251d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 17:15:55 +0800 Subject: [PATCH 424/438] Use Thread.currentThread() for group Simplify CustomizedThreadFactory by replacing the static import of System.getSecurityManager with Thread.currentThread, and remove the SecurityManager conditional. The thread group is now always obtained from the current thread (currentThread().getThreadGroup()), simplifying the logic for constructing the thread factory. --- .../io/microsphere/concurrent/CustomizedThreadFactory.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/concurrent/CustomizedThreadFactory.java b/microsphere-java-core/src/main/java/io/microsphere/concurrent/CustomizedThreadFactory.java index 7632414a0..0c514ff7a 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/concurrent/CustomizedThreadFactory.java +++ b/microsphere-java-core/src/main/java/io/microsphere/concurrent/CustomizedThreadFactory.java @@ -19,7 +19,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; -import static java.lang.System.getSecurityManager; +import static java.lang.Thread.currentThread; /** * A {@link ThreadFactory} implementation that creates threads with customized attributes, @@ -67,9 +67,7 @@ public class CustomizedThreadFactory implements ThreadFactory { private final long stackSize; protected CustomizedThreadFactory(String namePrefix, boolean daemon, int priority, long stackSize) { - SecurityManager s = getSecurityManager(); - this.group = (s != null) ? s.getThreadGroup() : - Thread.currentThread().getThreadGroup(); + this.group = currentThread().getThreadGroup(); this.threadNumber = new AtomicInteger(1); this.namePrefix = namePrefix + "-thread-"; this.daemon = daemon; From 8eed8ad91206454ee6ff350968b12dc9611b6cd4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 17:27:48 +0800 Subject: [PATCH 425/438] Expose resolveArguments and add unit tests Change resolveArguments visibility from private to package-private so it can be unit tested, and add ExceptionUtilsTest#testResolveArguments to verify behavior for exceptions with null message, with a message, and with an explicit cause. Also add required imports (ArrayUtils.ofArray and Assertions.assertArrayEquals) used by the new test. --- .../io/microsphere/util/ExceptionUtils.java | 4 ++-- .../microsphere/util/ExceptionUtilsTest.java | 24 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ExceptionUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ExceptionUtils.java index 287ed3008..da7e89dd9 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ExceptionUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ExceptionUtils.java @@ -301,7 +301,7 @@ public static TT throwTarget(T sourc throw wrap(source, thrownType); } - private static Object[] resolveArguments(T source) { + static Object[] resolveArguments(T source) { String message = source.getMessage(); Throwable cause = source.getCause() == null ? source : source.getCause(); return message == null ? new Object[]{cause} : new Object[]{message, cause}; @@ -309,4 +309,4 @@ private static Object[] resolveArguments(T source) { private ExceptionUtils() { } -} +} \ No newline at end of file diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ExceptionUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ExceptionUtilsTest.java index f3507b77a..1bb91a0fc 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ExceptionUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ExceptionUtilsTest.java @@ -21,10 +21,13 @@ import java.sql.SQLException; import static io.microsphere.text.FormatUtils.format; +import static io.microsphere.util.ArrayUtils.ofArray; import static io.microsphere.util.ExceptionUtils.create; import static io.microsphere.util.ExceptionUtils.getStackTrace; +import static io.microsphere.util.ExceptionUtils.resolveArguments; import static io.microsphere.util.ExceptionUtils.throwTarget; import static io.microsphere.util.ExceptionUtils.wrap; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -102,6 +105,24 @@ void testThrowTarget() { assertThrows(RuntimeException.class, () -> throwTarget(new Exception("Hello,World"), RuntimeException.class)); } + @Test + void testResolveArguments() { + Exception e = new Exception(); + + Object[] arguments = resolveArguments(e); + assertArrayEquals(ofArray(e), arguments); + + String message = "For testing..."; + e = new Exception(message); + arguments = resolveArguments(e); + assertArrayEquals(ofArray(message, e), arguments); + + Exception cause = new Exception(); + e = new Exception(message, cause); + arguments = resolveArguments(e); + assertArrayEquals(ofArray(message, cause), arguments); + } + private void assertWrap(T source, Class thrownType, Class causeType) { TT throwable = wrap(source, thrownType); assertTrue(thrownType.isAssignableFrom(throwable.getClass())); @@ -139,7 +160,6 @@ private void assertCreate(Class throwableClass, String assertEquals(cause, t.getCause()); } - private void assertCreate(Class throwableClass, Throwable cause, String messagePattern, Object... args) { String message = format(messagePattern, args); T t = create(throwableClass, cause, messagePattern, args); @@ -147,4 +167,4 @@ private void assertCreate(Class throwableClass, Throwab assertEquals(cause, t.getCause()); } -} +} \ No newline at end of file From 99a048231c3db2a5f8f3baaa5b3ea5290b631619 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 17:29:11 +0800 Subject: [PATCH 426/438] Change missing MethodHandle log to TRACE Reduce log severity when a MethodHandle can't be found in MethodHandlesLookupUtils by switching from WARN to TRACE and updating the guard from isWarnEnabled() to isTraceEnabled(). This lowers noise for expected/missing lookups while preserving the detailed message for trace-level debugging. --- .../java/io/microsphere/invoke/MethodHandlesLookupUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandlesLookupUtils.java b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandlesLookupUtils.java index 2fe15dd7c..ed3a4eb73 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandlesLookupUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/invoke/MethodHandlesLookupUtils.java @@ -119,8 +119,8 @@ protected static MethodHandle find(MethodHandles.Lookup lookup, Method method, MethodType methodType = isEmpty(parameterTypes) ? methodType(returnType) : methodType(returnType, parameterTypes); return execute(lookup, methodType, function, (l, mt, e) -> { Logger logger = getLogger(MethodHandlesLookupUtils.class); - if (logger.isWarnEnabled()) { - logger.warn("The MethodHandle can't be found by Lookup[{}] on the method : {}", l, method, e); + if (logger.isTraceEnabled()) { + logger.trace("The MethodHandle can't be found by Lookup[{}] on the method : {}", l, method, e); } return null; }); From dd527d7252cb7b6d373735f71d1ce21947e51b62 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 17:33:30 +0800 Subject: [PATCH 427/438] Update MethodKey tests for valueOf overloads Modify tests in MethodUtilsTest to use MethodKey instances for String.valueOf(boolean) and String.valueOf(char) instead of toString. Add an assertion to ensure the two valueOf overload keys are not equal, and change the final inequality check to compare against 'this' (a non-MethodKey) rather than the literal "String". These changes improve coverage of MethodKey comparisons by name and parameter types. --- .../test/java/io/microsphere/reflect/MethodUtilsTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java index 62e8a4345..3a89625dc 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java @@ -756,15 +756,15 @@ void testBuildKey() { assertMethodKey(ReflectionTest.class, "publicMethod", int.class); assertMethodKey(Appendable.class, "append", CharSequence.class, int.class, int.class); - - MethodKey key1 = new MethodKey(String.class, "toString"); + MethodKey key = new MethodKey(String.class, "valueOf", boolean.class); + MethodKey key1 = new MethodKey(String.class, "valueOf", char.class); MethodKey key2 = new MethodKey(String.class, "equals", Object.class); MethodKey key3 = new MethodKey(Object.class, "equals", Object.class); - + assertNotEquals(key, key1); assertNotEquals(key1, key2); assertNotEquals(key1, key3); - assertNotEquals(key1, "String"); + assertNotEquals(key1, this); } @Test From 3539dfe919c766ecc04364edb3cfe25b981abfbb Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 17:47:13 +0800 Subject: [PATCH 428/438] Add test for numberToString(1.23) Add an assertion in JSONObjectTest to verify numberToString(Double.valueOf(1.23)) returns '1.23'. This ensures correct formatting of positive non-integer doubles and guards against regressions in numeric string conversion. --- .../src/test/java/io/microsphere/json/JSONObjectTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/json/JSONObjectTest.java b/microsphere-java-core/src/test/java/io/microsphere/json/JSONObjectTest.java index 792e69457..27577d142 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/json/JSONObjectTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/json/JSONObjectTest.java @@ -443,6 +443,7 @@ void testNumberToString() throws JSONException { assertEquals("0", numberToString(Double.valueOf(0.0))); assertEquals("0", numberToString(Integer.valueOf(0))); assertEquals("-0", numberToString(NEGATIVE_ZERO)); + assertEquals("1.23", numberToString(Double.valueOf(1.23))); } @Test From fbd8edd3aa85bce89cefd0dd509a2f044418aba0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 19:05:41 +0800 Subject: [PATCH 429/438] Extract getDefaultClassLoader and add tests Introduce a static getDefaultClassLoader(ClassLoader) helper to centralize null->system ClassLoader resolution and update callers to use it. Add unit test testGetDefaultClassLoaderWithNull to verify behavior, add an extra null assertion in findLoadedClass-related tests, and fix a minor test formatting/brace placement. --- .../java/io/microsphere/util/ClassLoaderUtils.java | 4 ++++ .../io/microsphere/util/ClassLoaderUtilsTest.java | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java index c029fb6f4..2ef00bbf3 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java @@ -213,6 +213,10 @@ public static ClassLoader getDefaultClassLoader() { if (classLoader == null) { classLoader = ClassLoaderUtils.class.getClassLoader(); } + return getDefaultClassLoader(classLoader); + } + + static ClassLoader getDefaultClassLoader(ClassLoader classLoader) { // classLoader is null indicates the bootstrap ClassLoader return classLoader == null ? getSystemClassLoader() : classLoader; } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsTest.java index 4165a1a0d..cffa7782d 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ClassLoaderUtilsTest.java @@ -208,6 +208,11 @@ void testGetDefaultClassLoader() { } } + @Test + void testGetDefaultClassLoaderWithNull() { + assertSame(getSystemClassLoader(), getDefaultClassLoader(null)); + } + @Test void testGetClassLoader() { ClassLoader classLoader = currentClass.getClassLoader(); @@ -310,6 +315,9 @@ void testFindLoadedClass() { type = findLoadedClass(classLoader, "java/lang/String.class"); assertNull(type); + + type = findLoadedClass(classLoader, "com.acme.NotFound"); + assertNull(type); } @Test @@ -702,7 +710,8 @@ void testClassResourceType() { assertEquals("io/microsphere/util/ClassLoaderUtilsTest.class", CLASS.normalize(name)); assertNull(CLASS.resolve(null)); - assertNull(CLASS.resolve(name));} + assertNull(CLASS.resolve(name)); + } @Test void testPackageResourceType() { From f61d1ccc27bf647389cf8d8320dd7cdb4932b35a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 19:09:52 +0800 Subject: [PATCH 430/438] Add shutdownHookThreadsMap(Class) overload and tests Introduce a package-private overload shutdownHookThreadsMap(Class) and make the existing private shutdownHookThreadsMap() delegate to it. This enables tests to invoke the helper directly (including passing null) to verify behavior. Updated tests to import shutdownHookThreadsMap and emptyMap, assert that addShutdownHookCallback(null) returns false, and add testShutdownHookThreadsMapWithNullClass to confirm it returns the same empty map when given null. --- .../main/java/io/microsphere/util/ShutdownHookUtils.java | 4 ++++ .../java/io/microsphere/util/ShutdownHookUtilsTest.java | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/microsphere-java-core/src/main/java/io/microsphere/util/ShutdownHookUtils.java b/microsphere-java-core/src/main/java/io/microsphere/util/ShutdownHookUtils.java index 1bbe0ccf5..68eb9d2c0 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/util/ShutdownHookUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/util/ShutdownHookUtils.java @@ -379,6 +379,10 @@ static void clearShutdownHookCallbacks() { @Nonnull private static Map shutdownHookThreadsMap() { Class applicationShutdownHooksClass = resolveClass(TARGET_CLASS_NAME, getSystemClassLoader()); + return shutdownHookThreadsMap(applicationShutdownHooksClass); + } + + static Map shutdownHookThreadsMap(Class applicationShutdownHooksClass) { return applicationShutdownHooksClass == null ? emptyMap() : getStaticFieldValue(applicationShutdownHooksClass, HOOKS_FIELD_NAME); } diff --git a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java index 0009475d6..ce79b62fd 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/util/ShutdownHookUtilsTest.java @@ -32,8 +32,11 @@ import static io.microsphere.util.ShutdownHookUtils.registerShutdownHook; import static io.microsphere.util.ShutdownHookUtils.removeShutdownHookCallback; import static io.microsphere.util.ShutdownHookUtils.shutdownHookCallbacks; +import static io.microsphere.util.ShutdownHookUtils.shutdownHookThreadsMap; +import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -87,6 +90,8 @@ void testAddShutdownHookCallback() { ShutdownHookCallback shutdownHookCallback = (ShutdownHookCallback) shutdownHookCallbacks.poll(); assertEquals(i, shutdownHookCallback.getPriority()); } + + assertFalse(addShutdownHookCallback(null)); } @Test @@ -146,4 +151,8 @@ public void run() { thread.start(); } + @Test + void testShutdownHookThreadsMapWithNullClass() { + assertSame(emptyMap(), shutdownHookThreadsMap(null)); + } } \ No newline at end of file From b5a6ebd32a07e4a43b61d5b5280e33039417b00c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 20:15:26 +0800 Subject: [PATCH 431/438] Add logging and robust listener loading Introduce Logger and safer service loading in AbstractEventDispatcher: use getDefaultClassLoader and execute(...) to swallow errors with trace logging, add logger field, replace direct null-check with assertNotNull (documented as IllegalArgumentException), refactor doInListener by adding an overload that accepts an event type and delegating to it, and update imports accordingly. Update unit tests to match behavior changes: add setup, assert IllegalArgumentException for null executor, add tests for loading listeners, classloader-failure resilience, getExecutor, and doInListener(null) handling. --- .../event/AbstractEventDispatcher.java | 30 ++++++----- .../event/AbstractEventDispatcherTest.java | 51 ++++++++++++++++++- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/microsphere-java-core/src/main/java/io/microsphere/event/AbstractEventDispatcher.java b/microsphere-java-core/src/main/java/io/microsphere/event/AbstractEventDispatcher.java index 898dfa2ca..4baf73330 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/event/AbstractEventDispatcher.java +++ b/microsphere-java-core/src/main/java/io/microsphere/event/AbstractEventDispatcher.java @@ -18,6 +18,7 @@ import io.microsphere.annotation.Immutable; import io.microsphere.lang.Prioritized; +import io.microsphere.logging.Logger; import java.util.Collection; import java.util.LinkedList; @@ -32,6 +33,10 @@ import java.util.stream.Stream; import static io.microsphere.event.EventListener.findEventType; +import static io.microsphere.lang.function.ThrowableAction.execute; +import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.util.Assert.assertNotNull; +import static io.microsphere.util.ClassLoaderUtils.getDefaultClassLoader; import static io.microsphere.util.ServiceLoaderUtils.loadServicesList; import static java.util.Collections.sort; import static java.util.Collections.unmodifiableList; @@ -87,6 +92,8 @@ public abstract class AbstractEventDispatcher implements EventDispatcher { private final Object mutex = new Object(); + protected final Logger logger = getLogger(this.getClass()); + private final ConcurrentMap, List> listenersCache = new ConcurrentHashMap<>(); private final Executor executor; @@ -95,12 +102,10 @@ public abstract class AbstractEventDispatcher implements EventDispatcher { * Constructor with an instance of {@link Executor} * * @param executor {@link Executor} - * @throws NullPointerException executor is null + * @throws IllegalArgumentException executor is null */ protected AbstractEventDispatcher(Executor executor) { - if (executor == null) { - throw new NullPointerException("executor must not be null"); - } + assertNotNull(executor, () -> "executor must not be null"); this.executor = executor; this.loadEventListenerInstances(); } @@ -181,7 +186,10 @@ public final Executor getExecutor() { } protected void doInListener(EventListener listener, Consumer> consumer) { - Class eventType = findEventType(listener); + doInListener(findEventType(listener), consumer); + } + + void doInListener(Class eventType, Consumer> consumer) { if (eventType != null) { synchronized (mutex) { List listeners = listenersCache.computeIfAbsent(eventType, e -> new LinkedList<>()); @@ -202,13 +210,9 @@ protected void doInListener(EventListener listener, Consumer loadServicesList(EventListener.class, getDefaultClassLoader()) + .stream() + .sorted() + .forEach(this::addEventListener), e -> logger.trace(e.getMessage(), e)); } } diff --git a/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventDispatcherTest.java b/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventDispatcherTest.java index b83864a29..ab63b0f5a 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventDispatcherTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/event/AbstractEventDispatcherTest.java @@ -1,7 +1,17 @@ package io.microsphere.event; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; +import java.util.concurrent.Executor; + +import static java.lang.Thread.currentThread; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; /** @@ -13,10 +23,49 @@ */ class AbstractEventDispatcherTest { + private static final Executor executor = Runnable::run; + + private AbstractEventDispatcher eventDispatcher; + + @BeforeEach + void setUp() { + this.eventDispatcher = new AbstractEventDispatcher(executor) { + }; + } + @Test void testConstructorOnFailed() { - assertThrows(NullPointerException.class, () -> new AbstractEventDispatcher(null) { + assertThrows(IllegalArgumentException.class, () -> new AbstractEventDispatcher(null) { }); } + @Test + void testLoadEventListenerInstances() { + this.eventDispatcher.loadEventListenerInstances(); + List> eventListeners = this.eventDispatcher.getAllEventListeners(); + assertFalse(eventListeners.isEmpty()); + } + + @Test + void testLoadEventListenerInstancesOnFailed() { + ClassLoader classLoader = currentThread().getContextClassLoader(); + try { + URLClassLoader newClassLoader = new URLClassLoader(new URL[0], null); + currentThread().setContextClassLoader(newClassLoader); + assertDoesNotThrow(this.eventDispatcher::loadEventListenerInstances); + } finally { + currentThread().setContextClassLoader(classLoader); + } + } + + @Test + void testGetExecutor() { + assertSame(executor, this.eventDispatcher.getExecutor()); + } + + @Test + void testDoInListener() { + assertDoesNotThrow(() -> this.eventDispatcher.doInListener((Class) null, eventListeners -> { + })); + } } \ No newline at end of file From dff45b41ae96729d920c5240344d1e814cad6434 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 20:21:58 +0800 Subject: [PATCH 432/438] Use Boolean.valueOf in test assertion Wrap definition.isPresent() with Boolean.valueOf in AbstractReflectiveDefinitionTest and add a static import for java.lang.Boolean.valueOf. This makes the boxing explicit when passing the boolean result to assertNotNull, clarifying intent in the test. --- .../microsphere/reflect/AbstractReflectiveDefinitionTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java index 48b3095af..fe20c3c4f 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/AbstractReflectiveDefinitionTest.java @@ -15,6 +15,7 @@ import static io.microsphere.reflect.ConstructorUtils.findConstructor; import static io.microsphere.util.ArrayUtils.ofArray; import static io.microsphere.util.Version.ofVersion; +import static java.lang.Boolean.valueOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -118,7 +119,7 @@ void testIsDeprecated() { @Test void testIsPresent() { for (D definition : definitions) { - assertNotNull(definition.isPresent()); + assertNotNull(valueOf(definition.isPresent())); } } From d19a4782b5d344f9f41949fdb89a49f100405308 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 20:26:51 +0800 Subject: [PATCH 433/438] Simplify resource-not-found test Refactor testOpenConnectionOnResourceNotFound: add MalformedURLException to the test signature, move URL construction out of the lambda, and replace the lambda with a method reference (assertThrows(IOException.class, url::openStream)). Also add the MalformedURLException import to compile and improve readability. --- .../java/io/microsphere/net/classpath/HandlerTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/net/classpath/HandlerTest.java b/microsphere-java-core/src/test/java/io/microsphere/net/classpath/HandlerTest.java index b1f2be416..b75cf2915 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/net/classpath/HandlerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/net/classpath/HandlerTest.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; @@ -55,11 +56,9 @@ protected void testOpenConnection(URL url, Proxy proxy) throws IOException { } @Test - void testOpenConnectionOnResourceNotFound() { - assertThrows(IOException.class, () -> { - URL url = new URL(NOT_FOUND_URL); - url.openStream(); - }); + void testOpenConnectionOnResourceNotFound() throws MalformedURLException { + URL url = new URL(NOT_FOUND_URL); + assertThrows(IOException.class, url::openStream); } @Override From e26ccbea44ac42d8d15ffd23999b85f7fb5b40ef Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 20:34:09 +0800 Subject: [PATCH 434/438] Use explicit equals assertions in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace assertEquals/assertNotEquals usages with explicit assertTrue(...equals(...)) and assertFalse(...equals(...)) in MethodUtilsTest. This makes the intent explicit that the equals(Object) method is being tested (avoiding any ambiguity from generic assertion overloads) — two assertions updated. --- .../src/test/java/io/microsphere/reflect/MethodUtilsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java index 3a89625dc..ccc6bede2 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/MethodUtilsTest.java @@ -764,7 +764,7 @@ void testBuildKey() { assertNotEquals(key, key1); assertNotEquals(key1, key2); assertNotEquals(key1, key3); - assertNotEquals(key1, this); + assertFalse(key1.equals(this)); } @Test @@ -779,7 +779,7 @@ private void assertMethodKey(Class declaredClass, String methodName, Class MethodKey methodKey1 = buildKey(declaredClass, methodName, parameterTypes); MethodKey methodKey2 = buildKey(declaredClass, methodName, parameterTypes.length == 0 ? null : parameterTypes); - assertEquals(methodKey1, methodKey1); + assertTrue(methodKey1.equals(methodKey1)); assertEquals(methodKey1, methodKey2); assertEquals(methodKey1.hashCode(), methodKey2.hashCode()); assertEquals(methodKey1.toString(), methodKey2.toString()); From ffaa3202cfd92ad42b24ee14930b014a962f9a7c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 20:46:29 +0800 Subject: [PATCH 435/438] Use assertDoesNotThrow in ThrowableConsumerTest Wrap direct execute calls in assertDoesNotThrow and add the import to explicitly assert that no exceptions are thrown during the tests. Updated several test methods (testExecute1, testExecute2, testExecute3, testExecute3OnHandleException) to use the assertion wrapper. --- .../lang/function/ThrowableConsumerTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableConsumerTest.java b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableConsumerTest.java index dcae2fee8..f56f7e1d8 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableConsumerTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/lang/function/ThrowableConsumerTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import static io.microsphere.lang.function.ThrowableConsumer.execute; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; /** @@ -37,7 +38,7 @@ class ThrowableConsumerTest { @Test void testExecute1() { - consumer.execute("Hello,World"); + assertDoesNotThrow(() -> consumer.execute("Hello,World")); } @Test @@ -47,7 +48,7 @@ void testExecute1OnException() { @Test void testExecute2() { - execute("Hello,World", consumer); + assertDoesNotThrow(() -> execute("Hello,World", consumer)); } @Test @@ -57,14 +58,14 @@ void testExecute2OnException() { @Test void testExecute3() { - execute("Hello,World", consumer, (t, e) -> { - }); + assertDoesNotThrow(() -> execute("Hello,World", consumer, (t, e) -> { + })); } @Test void testExecute3OnHandleException() { - execute("For testing", throwableConsumer, (t, e) -> { - }); + assertDoesNotThrow(() -> execute("For testing", throwableConsumer, (t, e) -> { + })); } @Test From a2be7fe61cc63ed43c691dd0edaf53e67ee79a0d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 20:46:58 +0800 Subject: [PATCH 436/438] Annotate testRemove with @Override Add @Override to the testRemove() method in ReversedDequeTest to explicitly mark it as overriding the superclass implementation, enabling compiler checks and improving code clarity. No functional change to test behavior. --- .../test/java/io/microsphere/collection/ReversedDequeTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/collection/ReversedDequeTest.java b/microsphere-java-core/src/test/java/io/microsphere/collection/ReversedDequeTest.java index c820661d6..c82921028 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/collection/ReversedDequeTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/collection/ReversedDequeTest.java @@ -71,6 +71,7 @@ void testParallelStream() { } @Test + @Override void testRemove() { this.instance.remove(null); super.testRemove(); From f46af6c32d515cabcff764722c244f1f65a6ae24 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 20:48:50 +0800 Subject: [PATCH 437/438] Add @Override to testEquals Add the @Override annotation to the testEquals method in ReflectiveDefinitionTest to explicitly mark that it overrides the superclass implementation. This clarifies intent and enables compile-time override checking; no functional behavior change. --- .../java/io/microsphere/reflect/ReflectiveDefinitionTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java index f891635a6..7a4fdc020 100644 --- a/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java +++ b/microsphere-java-core/src/test/java/io/microsphere/reflect/ReflectiveDefinitionTest.java @@ -42,6 +42,7 @@ protected List getTailConstructorArguments() { } @Test + @Override void testEquals() { super.testEquals(); ReflectiveDefinitionImpl df1 = new ReflectiveDefinitionImpl(SINCE, DEPRECATION, "df1"); From 1a250be3d179a205caf54fb9871538bde10170fc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 4 Mar 2026 21:12:35 +0800 Subject: [PATCH 438/438] Update README with new modules and Javadocs Synchronize README module list and docs with project structure: add microsphere-java-annotations, microsphere-java-model, microsphere-jdk-tools, and microsphere-java-test; reorder and clarify the modules table; add 'Tools' and 'Testing' to features; update Javadoc links for the new modules. Also include minor formatting/whitespace fixes in XML examples for readability. --- README.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 174839178..97e146287 100644 --- a/README.md +++ b/README.md @@ -37,23 +37,30 @@ frameworks like Spring, making it a versatile addition to any Java developer's t - JMX - Versioning - Annotation processing +- Tools +- Testing ## Modules The framework is organized into several key modules: - Module | Purpose -----------------------------------|------------------------------------------------------------------------------------------------- - microsphere-java-core | Provides core utilities across various domains like annotations, collections, concurrency, etc. - microsphere-annotation-processor | Offers annotation processing capabilities for compile-time code generation - microsphere-java-dependencies | Manages dependency versions across the project - microsphere-java-parent | Parent POM with shared configurations + Module | Purpose +----------------------------------|----------------------------------------------------------------------------------------------------- + microsphere-java-annotations | Provides the common annotations for Microsphere Java projects. + microsphere-java-core | Provides the core utilities across various domains like annotations, collections, concurrency, etc. + microsphere-java-model | Provides the components and utilities across Java Language Model API. + microsphere-jdk-tools | Offers the components for Java tools. + microsphere-annotation-processor | Offers annotation processing capabilities for compile-time code generation. + microsphere-java-test | Provides the models and components for Java testing. + microsphere-java-dependencies | Manages dependency versions across the project. + microsphere-java-parent | Parent POM with shared configurations. ## Getting Started The easiest way to get started is by adding the Microsphere Java BOM (Bill of Materials) to your project's pom.xml: ```xml + ... @@ -73,6 +80,7 @@ The easiest way to get started is by adding the Microsphere Java BOM (Bill of Ma Then add the specific modules you need: ```xml + @@ -159,8 +167,12 @@ We welcome your contributions! Please read [Code of Conduct](./CODE_OF_CONDUCT.m ### JavaDoc +- [microsphere-java-annotations](https://javadoc.io/doc/io.github.microsphere-projects/microsphere-java-annotations) - [microsphere-java-core](https://javadoc.io/doc/io.github.microsphere-projects/microsphere-java-core) +- [microsphere-java-model](https://javadoc.io/doc/io.github.microsphere-projects/microsphere-java-model) +- [microsphere-jdk-tools](https://javadoc.io/doc/io.github.microsphere-projects/microsphere-jdk-tools) - [microsphere-annotation-processor](https://javadoc.io/doc/io.github.microsphere-projects/microsphere-annotation-processor) +- [microsphere-java-test](https://javadoc.io/doc/io.github.microsphere-projects/microsphere-java-test) ## License