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
相关产品推荐
相关产品推荐

