You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中使用Antlr4 TreeViewer如何实现JTree式层级树形结构?

实现ANTLR解析树的交互式JTree视图

当然可以实现你想要的可展开折叠的交互式树形结构!ANTLR的ParseTree本身就是层级化的结构,我们只需要把它转换成Swing JTree能识别的TreeNode结构,就能得到和普通JTree一样的交互体验了。

步骤1:编写ParseTree到JTree节点的转换器

首先我们需要一个工具类,递归遍历ANTLR的解析树,将每个节点转换成Swing的DefaultMutableTreeNode:

import javax.swing.tree.DefaultMutableTreeNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.ParserRuleContext;

public class ParseTreeToJTreeConverter {
    // 带规则名称的转换方法,让非终端节点显示规则名更友好
    public static DefaultMutableTreeNode convert(ParseTree parseTree, String[] ruleNames) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode(getNodeLabel(parseTree, ruleNames));
        
        // 递归处理所有子节点
        for (int i = 0; i < parseTree.getChildCount(); i++) {
            ParseTree child = parseTree.getChild(i);
            node.add(convert(child, ruleNames));
        }
        
        return node;
    }

    private static String getNodeLabel(ParseTree node, String[] ruleNames) {
        if (node instanceof TerminalNode) {
            // 终端节点显示文本和对应的token类型
            TerminalNode terminal = (TerminalNode) node;
            return String.format("%s (token type: %d)", terminal.getText(), terminal.getSymbol().getType());
        } else if (node instanceof ParserRuleContext) {
            // 非终端节点显示对应的规则名称
            ParserRuleContext ctx = (ParserRuleContext) node;
            return ruleNames[ctx.getRuleIndex()];
        } else {
            // 其他节点类型显示简化后的类名
            return node.getClass().getSimpleName().replace("Context", "");
        }
    }
}

步骤2:修改测试代码,替换TreeViewer为JTree

接下来修改你原来的HelloTestDrive类,把生成静态树形图的逻辑换成生成交互式JTree的逻辑:

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.tree.ParseTree;
import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
import java.util.Arrays;

public class HelloTestDrive {
    public static void main(String[] args) {
        // 这部分和你原来的代码一致,生成ParseTree
        CharStream stream = new ANTLRInputStream("hello antlr");
        HelloLexer lexer = new HelloLexer(stream);
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        HelloParser parser = new HelloParser(tokenStream);
        ParseTree tree = parser.r();
        
        // 控制台打印解析树(保留原来的调试逻辑)
        System.out.println(tree.toStringTree(parser));
        
        // 转换ParseTree为JTree可用的节点模型
        DefaultMutableTreeNode rootNode = ParseTreeToJTreeConverter.convert(tree, parser.getRuleNames());
        DefaultTreeModel treeModel = new DefaultTreeModel(rootNode);
        
        // 创建JTree并配置交互行为
        JTree interactiveTree = new JTree(treeModel);
        interactiveTree.setRootVisible(true);
        interactiveTree.expandRow(0); // 默认展开根节点
        interactiveTree.setShowsRootHandles(true); // 显示展开/折叠手柄
        
        // 构建GUI界面,用滚动面板包裹JTree适配大的树形结构
        JFrame frame = new JFrame("ANTLR Interactive Parse Tree");
        JScrollPane scrollPane = new JScrollPane(interactiveTree);
        frame.add(scrollPane);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(450, 350);
        frame.setLocationRelativeTo(null); // 让窗口居中显示
        frame.setVisible(true);
    }
}

额外优化提示

  • 你可以自定义getNodeLabel方法的输出格式,比如调整终端节点的显示内容,或者给不同类型的节点添加图标。
  • 如果需要支持节点双击事件、右键菜单等交互,可以给JTree添加对应的监听器,比如MouseListener。
  • 对于大型解析树,JTree的滚动和展开折叠性能比静态树形图要好很多,更适合复杂语法的调试。

内容的提问来源于stack exchange,提问作者Rodrigo Arriaza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:49:24