如何在Java注释处理器中解析SwitchTree表达式类型?

发布于 2025-02-10 12:54:23 字数 818 浏览 2 评论 0 原文

让我们考虑以下代码:

switch ( <em>switchTreeExpression</em> ) {
    <em>cases</em>
}

我想找出 SwitchTree Expression 是什么类型。

我有以下代码草稿:

...
MethodTree methodTree = trees.getTree(method);
BlockTree blockTree = methodTree.getBody();

for (StatementTree statementTree : blockTree.getStatements()) {
    if (statementTree.getKind() == Tree.Kind.SWITCH) {
        SwitchTree switchTree = (SwitchTree) statementTree;
        ExpressionTree switchTreeExpression = switchTree.getExpression();
        // I need to get the type of *switchTreeExpression* here
    }
}

有趣的是,我可以从 .class 文件中获取 switchtree Expression 的类型。但是,似乎没有办法在这一注释处理阶段获取当前类的字节代码(如果我错了,我很高兴只能获取字节代码并使用 objectweb asm 库进行分析。 )。

Let's consider the following code:

switch ( <em>switchTreeExpression</em> ) {
    <em>cases</em>
}

I want to find out, what type for switchTreeExpression is .

I have the following code draft:

...
MethodTree methodTree = trees.getTree(method);
BlockTree blockTree = methodTree.getBody();

for (StatementTree statementTree : blockTree.getStatements()) {
    if (statementTree.getKind() == Tree.Kind.SWITCH) {
        SwitchTree switchTree = (SwitchTree) statementTree;
        ExpressionTree switchTreeExpression = switchTree.getExpression();
        // I need to get the type of *switchTreeExpression* here
    }
}

It is interesting, that I can get the type of switchTreeExpression from .class file. However it seems that there is no way to get byte code of the current class in this phase of annotation processing (if I am wrong, I would be happy just get byte code and analyze it with ObjectWeb ASM library).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

開玄 2025-02-17 12:54:23

可能的解决方案

注释处理器

让我们考虑类型注释的注释处理器( @target(elementType.type))。

限制: processor.process()方法:无方法

注释处理发生在汇编时间表的特定点,在读取了命令行上指定的所有源文件和类之后,并分析了它们所包含的类型和成员,但是在任何方法的内容之前已分析。

克服限制:使用 com.sun.source.util.taskListener

该想法是处理类型元素分析完成事件。

  • processor.init()方法:注册任务侦听器,并使用捕获的注释类型元素处理类型元素分析完成事件。
  • processor.process()方法:捕获带注释的类型元素。

一些相关的引用:

关于实施方法的注意,

一些第三方依赖(库和框架)可用于实现注释处理器。

例如,已经提到的检查器框架。

一些相关引用:

请注意,检查器框架处理器使用 @supportedAnnotationTypes(“*”)

实施草案

让我们考虑实施草案,该草案不使用“实施方法注释”部分中提到的第三方依赖项。

注释处理器项目

Maven Project
<properties>
    <auto-service.version>1.0.1</auto-service.version>
</properties>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>com.google.auto.service</groupId>
                <artifactId>auto-service</artifactId>
                <version>${auto-service.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>
<dependency>
    <groupId>com.google.auto.service</groupId>
    <artifactId>auto-service-annotations</artifactId>
    <version>${auto-service.version}</version>
</dependency>
AbstractTyPeprocessor 类:基类:

我们介绍具有以下摘要方法的基类:

public abstract void processType(Trees trees, TypeElement typeElement, TreePath treePath);
import com.sun.source.util.JavacTask;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;

// NOTE: It is designed to work only with `@Target(ElementType.TYPE)` annotations!
public abstract class AbstractTypeProcessor extends AbstractProcessor {
    private final AnalyzeTaskListener analyzeTaskListener = new AnalyzeTaskListener(this);
    protected final Set<Name> remainingTypeElementNames = new HashSet<>();
    private Trees trees;

    protected AbstractTypeProcessor() {
    }

    @Override
    public synchronized void init(final ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        trees = Trees.instance(processingEnv);
        JavacTask.instance(processingEnv).addTaskListener(analyzeTaskListener);
    }

    @Override
    public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
        for (final TypeElement annotation : annotations) {
            final Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
            final Set<TypeElement> typeElements = ElementFilter.typesIn(annotatedElements);
            final List<Name> typeElementNames = typeElements.stream()
                .map(TypeElement::getQualifiedName)
                .toList();
            remainingTypeElementNames.addAll(typeElementNames);
        }
        System.out.println(
            String.format("Remaining type element names: %s", remainingTypeElementNames)
        );
        return false;
    }

    public abstract void processType(Trees trees, TypeElement typeElement, TreePath treePath);

    private void handleAnalyzedType(final TypeElement typeElement) {
        System.out.println(
            String.format("Handling analyzed type element: %s", typeElement)
        );
        if (!remainingTypeElementNames.remove(typeElement.getQualifiedName())) {
            return;
        }

        final TreePath treePath = trees.getPath(typeElement);
        processType(trees, typeElement, treePath);
    }

    private static final class AnalyzeTaskListener implements TaskListener {
        private final AbstractTypeProcessor processor;

        public AnalyzeTaskListener(final AbstractTypeProcessor processor) {
            this.processor = processor;
        }

        @Override
        public void finished(final TaskEvent e) {
            if (e.getKind() != TaskEvent.Kind.ANALYZE) {
                return;
            }

            processor.handleAnalyzedType(e.getTypeElement());
        }
    }
}
checkmethodbodies class>类:注释> code
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface CheckMethodBodies {
}
> chece> checkmethodbodiesprocessor class class class
import com.google.auto.service.AutoService;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import javax.annotation.processing.Processor;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

@SupportedAnnotationTypes("org.example.annotation.processor.CheckMethodBodies")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public final class CheckMethodBodiesProcessor extends AbstractTypeProcessor {
    @Override
    public void processType(final Trees trees, final TypeElement typeElement, final TreePath treePath) {
        final CompilationUnitTree compilationUnitTree = treePath.getCompilationUnit();
        final TestMethodTreePathScanner treePathScanner = new TestMethodTreePathScanner(trees, compilationUnitTree);
        treePathScanner.scan(compilationUnitTree, null);
    }

    private static final class TestMethodTreePathScanner extends TreePathScanner<Void, Void> {
        private final Trees trees;
        private final CompilationUnitTree compilationUnitTree;

        public TestMethodTreePathScanner(
            final Trees trees,
            final CompilationUnitTree compilationUnitTree
        ) {
            this.trees = trees;
            this.compilationUnitTree = compilationUnitTree;
        }

        @Override
        public Void visitMethod(final MethodTree node, final Void unused) {
            System.out.println(
                String.format("Visiting method: %s", node.getName())
            );

            final BlockTree blockTree = node.getBody();
            for (final StatementTree statementTree : blockTree.getStatements()) {
                if (statementTree.getKind() != Tree.Kind.SWITCH) {
                    continue;
                }

                final SwitchTree switchTree = (SwitchTree) statementTree;
                final ExpressionTree switchTreeExpression = switchTree.getExpression();
                System.out.println(
                    String.format("Switch tree expression: %s", switchTreeExpression)
                );

                final TreePath treePath = TreePath.getPath(compilationUnitTree, switchTreeExpression);
                final TypeMirror typeMirror = trees.getTypeMirror(treePath);
                System.out.println(
                    String.format("Tree mirror: %s", typeMirror)
                );
            }
            return null;
        }
    }
}

测试项目

maven项目,
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>org.example</groupId>
                <artifactId>annotation-processor</artifactId>
                <version>1.0.0-SNAPSHOT</version>
            </path>
        </annotationProcessorPaths>
        <showWarnings>true</showWarnings>
    </configuration>
</plugin>

以便能够使用注释类:

<dependency>
    <groupId>org.example</groupId>
    <artifactId>annotation-processor</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>
switcher 类:使用注释
import org.example.annotation.processor.CheckMethodBodies;

@CheckMethodBodies
public final class Switcher {
    public void theMethod() {
        final Integer value = 1;
        switch (value.toString() + "0" + "0") {
            case "100":
                System.out.println("Hundred!");
            default:
                System.out.println("Not hundred!");
        }
    }
}

测试

执行注释处理器项目的命令:

mvn clean install

执行测试项目的命令:

mvn clean compile

观察的输出注释处理器:

Remaining type element names: [org.example.annotation.processor.test.Switcher]
Remaining type element names: [org.example.annotation.processor.test.Switcher]
Handling analyzed type element: org.example.annotation.processor.test.Switcher
Visiting method: <init>
Visiting method: theMethod
Switch tree expression: (value.toString() + "00")
Tree mirror: java.lang.String

独立程序

可以在独立程序中使用JAVAC功能。

看来有必要获取树路径然后获取类型镜:

final CompilationUnitTree compilationUnitTree = <…>;
final ExpressionTree switchTreeExpression = <…>;

final TreePath treePath = TreePath.getPath(compilationUnitTree, switchTreeExpression);
final TypeMirror typeMirror = trees.getTypeMirror(treePath);

摘录文档: typemirror(java Platform se 8)

公共接口Typemirror
扩展 AnnotatedConstruct

代表Java编程语言中的一种类型。类型包括原始类型,已声明的类型(类和接口类型),数组类型,类型变量和NULL类型。还表示的是通配符类型参数,可执行文件的签名和返回类型,以及对应于软件包和关键字 void 的伪类型。

草稿实现

输入文件: switcher

public final class Switcher {
    public void theMethod() {
        final Integer value = 1;
        switch (value.toString() + "0" + "0") {
            case "100":
                System.out.println("Hundred!");
            default:
                System.out.println("Not hundred!");
        }
    }
}

program class

,请替换“/path/to/switcher.java” 用实际的文件路径值文件路径值。

import com.sun.source.tree.BlockTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

public final class Program {
    public static void main(final String[] args) throws IOException {
        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        final JavacTask task = (JavacTask) compiler.getTask(
            null,
            null,
            null,
            null,
            null,
            List.of(new TestFileObject())
        );
        final Iterable<? extends CompilationUnitTree> compilationUnitTrees = task.parse();
        task.analyze();
        final Trees trees = Trees.instance(task);

        for (final CompilationUnitTree compilationUnitTree : compilationUnitTrees) {
            final TestMethodTreePathScanner treePathScanner = new TestMethodTreePathScanner(trees, compilationUnitTree);
            treePathScanner.scan(compilationUnitTree, null);
        }
    }

    private static final class TestFileObject extends SimpleJavaFileObject {
        public TestFileObject() {
            super(URI.create("myfo:/Switcher.java"), JavaFileObject.Kind.SOURCE);
        }

        @Override
        public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException {
            return Files.readString(
                Path.of("/path/to/Switcher.java"),
                StandardCharsets.UTF_8
            );
        }
    }

    private static final class TestMethodTreePathScanner extends TreePathScanner<Void, Void> {
        private final Trees trees;
        private final CompilationUnitTree compilationUnitTree;

        public TestMethodTreePathScanner(
            final Trees trees,
            final CompilationUnitTree compilationUnitTree
        ) {
            this.trees = trees;
            this.compilationUnitTree = compilationUnitTree;
        }

        @Override
        public Void visitMethod(final MethodTree node, final Void unused) {
            final BlockTree blockTree = node.getBody();
            for (final StatementTree statementTree : blockTree.getStatements()) {
                if (statementTree.getKind() != Tree.Kind.SWITCH) {
                    continue;
                }

                final SwitchTree switchTree = (SwitchTree) statementTree;
                final ExpressionTree switchTreeExpression = switchTree.getExpression();
                System.out.println(
                    String.format("Switch tree expression: %s", switchTreeExpression)
                );

                final TreePath treePath = TreePath.getPath(compilationUnitTree, switchTreeExpression);
                final TypeMirror typeMirror = trees.getTypeMirror(treePath);
                System.out.println(
                    String.format("Tree mirror: %s", typeMirror)
                );
            }
            return null;
        }
    }
}

程序输出:

Switch tree expression: (value.toString() + "00")
Tree mirror: java.lang.String

Possible solutions

Annotation processor

Let's consider an annotation processor for the type annotations (@Target(ElementType.TYPE)).

Limitation: Processor.process() method: No method bodies

Processing Code:

Annotation processing occurs at a specific point in the timeline of a compilation, after all source files and classes specified on the command line have been read, and analyzed for the types and members they contain, but before the contents of any method bodies have been analyzed.

Overcoming limitation: Using com.sun.source.util.TaskListener

The idea is to handle the type element analysis completion events.

  • Processor.init() method: Register a task listener and handle the type element analysis completion events using the captured annotated type elements.
  • Processor.process() method: Capture the annotated type elements.

Some related references:

Note on implementation approaches

Some third-party dependencies (libraries and frameworks) may be used to implement an annotation processor.

For example, the already mentioned Checker Framework.

Some related references:

Please, note that the Checker Framework processors use @SupportedAnnotationTypes("*").

Draft implementation

Let's consider a draft implementation, which does not use third-party dependencies mentioned in the «Note on implementation approaches» section.

Annotation processor project

Maven project
<properties>
    <auto-service.version>1.0.1</auto-service.version>
</properties>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>com.google.auto.service</groupId>
                <artifactId>auto-service</artifactId>
                <version>${auto-service.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>
<dependency>
    <groupId>com.google.auto.service</groupId>
    <artifactId>auto-service-annotations</artifactId>
    <version>${auto-service.version}</version>
</dependency>
AbstractTypeProcessor class: Base class

Let's introduce the base class that has the following abstract method:

public abstract void processType(Trees trees, TypeElement typeElement, TreePath treePath);
import com.sun.source.util.JavacTask;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;

// NOTE: It is designed to work only with `@Target(ElementType.TYPE)` annotations!
public abstract class AbstractTypeProcessor extends AbstractProcessor {
    private final AnalyzeTaskListener analyzeTaskListener = new AnalyzeTaskListener(this);
    protected final Set<Name> remainingTypeElementNames = new HashSet<>();
    private Trees trees;

    protected AbstractTypeProcessor() {
    }

    @Override
    public synchronized void init(final ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        trees = Trees.instance(processingEnv);
        JavacTask.instance(processingEnv).addTaskListener(analyzeTaskListener);
    }

    @Override
    public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
        for (final TypeElement annotation : annotations) {
            final Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
            final Set<TypeElement> typeElements = ElementFilter.typesIn(annotatedElements);
            final List<Name> typeElementNames = typeElements.stream()
                .map(TypeElement::getQualifiedName)
                .toList();
            remainingTypeElementNames.addAll(typeElementNames);
        }
        System.out.println(
            String.format("Remaining type element names: %s", remainingTypeElementNames)
        );
        return false;
    }

    public abstract void processType(Trees trees, TypeElement typeElement, TreePath treePath);

    private void handleAnalyzedType(final TypeElement typeElement) {
        System.out.println(
            String.format("Handling analyzed type element: %s", typeElement)
        );
        if (!remainingTypeElementNames.remove(typeElement.getQualifiedName())) {
            return;
        }

        final TreePath treePath = trees.getPath(typeElement);
        processType(trees, typeElement, treePath);
    }

    private static final class AnalyzeTaskListener implements TaskListener {
        private final AbstractTypeProcessor processor;

        public AnalyzeTaskListener(final AbstractTypeProcessor processor) {
            this.processor = processor;
        }

        @Override
        public void finished(final TaskEvent e) {
            if (e.getKind() != TaskEvent.Kind.ANALYZE) {
                return;
            }

            processor.handleAnalyzedType(e.getTypeElement());
        }
    }
}
CheckMethodBodies class: Annotation class
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface CheckMethodBodies {
}
CheckMethodBodiesProcessor class: Annotation processor
import com.google.auto.service.AutoService;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import javax.annotation.processing.Processor;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

@SupportedAnnotationTypes("org.example.annotation.processor.CheckMethodBodies")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public final class CheckMethodBodiesProcessor extends AbstractTypeProcessor {
    @Override
    public void processType(final Trees trees, final TypeElement typeElement, final TreePath treePath) {
        final CompilationUnitTree compilationUnitTree = treePath.getCompilationUnit();
        final TestMethodTreePathScanner treePathScanner = new TestMethodTreePathScanner(trees, compilationUnitTree);
        treePathScanner.scan(compilationUnitTree, null);
    }

    private static final class TestMethodTreePathScanner extends TreePathScanner<Void, Void> {
        private final Trees trees;
        private final CompilationUnitTree compilationUnitTree;

        public TestMethodTreePathScanner(
            final Trees trees,
            final CompilationUnitTree compilationUnitTree
        ) {
            this.trees = trees;
            this.compilationUnitTree = compilationUnitTree;
        }

        @Override
        public Void visitMethod(final MethodTree node, final Void unused) {
            System.out.println(
                String.format("Visiting method: %s", node.getName())
            );

            final BlockTree blockTree = node.getBody();
            for (final StatementTree statementTree : blockTree.getStatements()) {
                if (statementTree.getKind() != Tree.Kind.SWITCH) {
                    continue;
                }

                final SwitchTree switchTree = (SwitchTree) statementTree;
                final ExpressionTree switchTreeExpression = switchTree.getExpression();
                System.out.println(
                    String.format("Switch tree expression: %s", switchTreeExpression)
                );

                final TreePath treePath = TreePath.getPath(compilationUnitTree, switchTreeExpression);
                final TypeMirror typeMirror = trees.getTypeMirror(treePath);
                System.out.println(
                    String.format("Tree mirror: %s", typeMirror)
                );
            }
            return null;
        }
    }
}

Test project

Maven project
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>org.example</groupId>
                <artifactId>annotation-processor</artifactId>
                <version>1.0.0-SNAPSHOT</version>
            </path>
        </annotationProcessorPaths>
        <showWarnings>true</showWarnings>
    </configuration>
</plugin>

To be able to use the annotation class:

<dependency>
    <groupId>org.example</groupId>
    <artifactId>annotation-processor</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>
Switcher class: Using annotation
import org.example.annotation.processor.CheckMethodBodies;

@CheckMethodBodies
public final class Switcher {
    public void theMethod() {
        final Integer value = 1;
        switch (value.toString() + "0" + "0") {
            case "100":
                System.out.println("Hundred!");
            default:
                System.out.println("Not hundred!");
        }
    }
}

Testing

Execute the command for the annotation processor project:

mvn clean install

Execute the command for the test project:

mvn clean compile

Observe the output of the annotation processor:

Remaining type element names: [org.example.annotation.processor.test.Switcher]
Remaining type element names: [org.example.annotation.processor.test.Switcher]
Handling analyzed type element: org.example.annotation.processor.test.Switcher
Visiting method: <init>
Visiting method: theMethod
Switch tree expression: (value.toString() + "00")
Tree mirror: java.lang.String

Stand-alone program

It is possible to use javac functionality in a stand-alone program.

It seems that it is necessary to get the tree path and then get the type mirror:

final CompilationUnitTree compilationUnitTree = <…>;
final ExpressionTree switchTreeExpression = <…>;

final TreePath treePath = TreePath.getPath(compilationUnitTree, switchTreeExpression);
final TypeMirror typeMirror = trees.getTypeMirror(treePath);

An excerpt from the documentation: TypeMirror (Java Platform SE 8 ):

public interface TypeMirror
extends AnnotatedConstruct

Represents a type in the Java programming language. Types include primitive types, declared types (class and interface types), array types, type variables, and the null type. Also represented are wildcard type arguments, the signature and return types of executables, and pseudo-types corresponding to packages and to the keyword void.

Draft implementation

Input file: Switcher class

public final class Switcher {
    public void theMethod() {
        final Integer value = 1;
        switch (value.toString() + "0" + "0") {
            case "100":
                System.out.println("Hundred!");
            default:
                System.out.println("Not hundred!");
        }
    }
}

Program class

Please, replace the "/path/to/Switcher.java" file path value with the actual file path value.

import com.sun.source.tree.BlockTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

public final class Program {
    public static void main(final String[] args) throws IOException {
        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        final JavacTask task = (JavacTask) compiler.getTask(
            null,
            null,
            null,
            null,
            null,
            List.of(new TestFileObject())
        );
        final Iterable<? extends CompilationUnitTree> compilationUnitTrees = task.parse();
        task.analyze();
        final Trees trees = Trees.instance(task);

        for (final CompilationUnitTree compilationUnitTree : compilationUnitTrees) {
            final TestMethodTreePathScanner treePathScanner = new TestMethodTreePathScanner(trees, compilationUnitTree);
            treePathScanner.scan(compilationUnitTree, null);
        }
    }

    private static final class TestFileObject extends SimpleJavaFileObject {
        public TestFileObject() {
            super(URI.create("myfo:/Switcher.java"), JavaFileObject.Kind.SOURCE);
        }

        @Override
        public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException {
            return Files.readString(
                Path.of("/path/to/Switcher.java"),
                StandardCharsets.UTF_8
            );
        }
    }

    private static final class TestMethodTreePathScanner extends TreePathScanner<Void, Void> {
        private final Trees trees;
        private final CompilationUnitTree compilationUnitTree;

        public TestMethodTreePathScanner(
            final Trees trees,
            final CompilationUnitTree compilationUnitTree
        ) {
            this.trees = trees;
            this.compilationUnitTree = compilationUnitTree;
        }

        @Override
        public Void visitMethod(final MethodTree node, final Void unused) {
            final BlockTree blockTree = node.getBody();
            for (final StatementTree statementTree : blockTree.getStatements()) {
                if (statementTree.getKind() != Tree.Kind.SWITCH) {
                    continue;
                }

                final SwitchTree switchTree = (SwitchTree) statementTree;
                final ExpressionTree switchTreeExpression = switchTree.getExpression();
                System.out.println(
                    String.format("Switch tree expression: %s", switchTreeExpression)
                );

                final TreePath treePath = TreePath.getPath(compilationUnitTree, switchTreeExpression);
                final TypeMirror typeMirror = trees.getTypeMirror(treePath);
                System.out.println(
                    String.format("Tree mirror: %s", typeMirror)
                );
            }
            return null;
        }
    }
}

The program output:

Switch tree expression: (value.toString() + "00")
Tree mirror: java.lang.String
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文