JavaParser入门:以编程方式分析Java代码

我最喜欢的事情之一是解析代码并对其执行自动操作。 因此,我开始为JavaParser做出贡献,并创建了两个相关项目: java-symbol-solver和Effectivejava 。 java_jp-1024x648

作为JavaParser的贡献者,我反复阅读了一些非常类似的问题,这些问题涉及从Java源代码提取信息。 因此,我认为我可以帮助提供一些简单的示例,以帮助您开始解析Java代码。

  • Github上提供了所有源代码: analyzer-java-code-examples

通用代码

使用JavaParser theere时,我们总是希望进行很多操作。 通常,我们希望对整个项目进行操作,因此在给定目录的情况下,我们将探索所有Java文件。 此类应有助于完成此任务:

package me.tomassetti.support;import java.io.File;public class DirExplorer {public interface FileHandler {void handle(int level, String path, File file);}public interface Filter {boolean interested(int level, String path, File file);}private FileHandler fileHandler;private Filter filter;public DirExplorer(Filter filter, FileHandler fileHandler) {this.filter = filter;this.fileHandler = fileHandler;}public void explore(File root) {explore(0, "", root);}private void explore(int level, String path, File file) {if (file.isDirectory()) {for (File child : file.listFiles()) {explore(level + 1, path + "/" + child.getName(), child);}} else {if (filter.interested(level, path, file)) {fileHandler.handle(level, path, file);}}}}

对于每个Java文件,我们首先要为每个Java文件构建一个抽象语法树(AST),然后对其进行导航。 这样做有两种主要策略:

  1. 使用访客:要在特定类型的AST节点上进行操作时,这是正确的策略
  2. 使用递归迭代器:这允许处理所有类型的节点

可以编写访问者扩展JavaParser中包含的类,而这是一个简单的节点迭代器:

package me.tomassetti.support;import com.github.javaparser.ast.Node;public class NodeIterator {public interface NodeHandler {boolean handle(Node node);}private NodeHandler nodeHandler;public NodeIterator(NodeHandler nodeHandler) {this.nodeHandler = nodeHandler;}public void explore(Node node) {if (nodeHandler.handle(node)) {for (Node child : node.getChildrenNodes()) {explore(child);}}}
}

现在,让我们看看如何使用此代码解决Stack Overflow上的一些问题。

如何从Java类中提取普通字符串中所有类的名称?

  • 在堆栈溢出时询问

寻找ClassOrInterfaceDeclaration节点可以解决此解决方案。 给定我们想要一种特定类型的节点,我们可以使用访客。 请注意,VoidVisitorAdapter允许传递任意参数。 在这种情况下,我们不需要这样做,因此我们指定对象类型,而在访问方法中将其忽略即可。

package me.tomassetti.examples;import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.google.common.base.Strings;
import me.tomassetti.support.DirExplorer;import java.io.File;
import java.io.IOException;public class ListClassesExample {public static void listClasses(File projectDir) {new DirExplorer((level, path, file) -> path.endsWith(".java"), (level, path, file) -> {System.out.println(path);System.out.println(Strings.repeat("=", path.length()));try {new VoidVisitorAdapter<Object>() {@Overridepublic void visit(ClassOrInterfaceDeclaration n, Object arg) {super.visit(n, arg);System.out.println(" * " + n.getName());}}.visit(JavaParser.parse(file), null);System.out.println(); // empty line} catch (ParseException | IOException e) {new RuntimeException(e);}}).explore(projectDir);}public static void main(String[] args) {File projectDir = new File("source_to_parse/junit-master");listClasses(projectDir);}
}

我们在JUnit的源代码上运行示例,并获得以下输出:

/src/test/java/org/junit/internal/MethodSorterTest.java
=======================================================* DummySortWithoutAnnotation* Super* Sub* DummySortWithDefault* DummySortJvm* DummySortWithNameAsc* MethodSorterTest/src/test/java/org/junit/internal/matchers/StacktracePrintingMatcherTest.java
=============================================================================* StacktracePrintingMatcherTest/src/test/java/org/junit/internal/matchers/ThrowableCauseMatcherTest.java
=========================================================================* ThrowableCauseMatcherTest... 
... many other lines follow

是否有Java代码解析器可以返回组成语句的行号?

  • 在堆栈溢出时询问

在这种情况下,我需要查找各种语句。 现在,有几个类扩展了Statement基类,因此我可以使用一个访问者,但我需要在几种访问方法中编写相同的代码,一个用于Statement的每个子类。 另外,我只想获取顶层语句,而不要获取其中的语句。 例如,一个for语句可以包含其他几个语句。 使用我们的自定义NodeIterator,我们可以轻松实现此逻辑。

package me.tomassetti.examples;import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.stmt.Statement;
import com.google.common.base.Strings;
import me.tomassetti.support.DirExplorer;
import me.tomassetti.support.NodeIterator;import java.io.File;
import java.io.IOException;public class StatementsLinesExample {public static void statementsByLine(File projectDir) {new DirExplorer((level, path, file) -> path.endsWith(".java"), (level, path, file) -> {System.out.println(path);System.out.println(Strings.repeat("=", path.length()));try {new NodeIterator(new NodeIterator.NodeHandler() {@Overridepublic boolean handle(Node node) {if (node instanceof Statement) {System.out.println(" [Lines " + node.getBeginLine() + " - " + node.getEndLine() + " ] " + node);return false;} else {return true;}}}).explore(JavaParser.parse(file));System.out.println(); // empty line} catch (ParseException | IOException e) {new RuntimeException(e);}}).explore(projectDir);}public static void main(String[] args) {File projectDir = new File("source_to_parse/junit-master");statementsByLine(projectDir);}
}

这是在JUnit的源代码上运行程序所获得的输出的一部分。

/src/test/java/org/junit/internal/matchers/ThrowableCauseMatcherTest.java
=========================================================================[Lines 12 - 17 ] {NullPointerException expectedCause = new NullPointerException("expected");Exception actual = new Exception(expectedCause);assertThat(actual, hasCause(is(expectedCause)));
}

您可能会注意到报告的语句跨5个,而不是报告的6个(12..17是6行)。 这是因为我们正在打印该语句的纯净版本,删除了白线,注释并设置了代码格式。

从Java代码中提取方法调用

  • 在堆栈溢出时询问

对于提取方法调用,我们可以再次使用Visitor,因此这非常简单,并且与我们看到的第一个示例非常相似。

package me.tomassetti.examples;import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.google.common.base.Strings;
import me.tomassetti.support.DirExplorer;import java.io.File;
import java.io.IOException;public class MethodCallsExample {public static void listMethodCalls(File projectDir) {new DirExplorer((level, path, file) -> path.endsWith(".java"), (level, path, file) -> {System.out.println(path);System.out.println(Strings.repeat("=", path.length()));try {new VoidVisitorAdapter<Object>() {@Overridepublic void visit(MethodCallExpr n, Object arg) {super.visit(n, arg);System.out.println(" [L " + n.getBeginLine() + "] " + n);}}.visit(JavaParser.parse(file), null);System.out.println(); // empty line} catch (ParseException | IOException e) {new RuntimeException(e);}}).explore(projectDir);}public static void main(String[] args) {File projectDir = new File("source_to_parse/junit-master");listMethodCalls(projectDir);}
}

如您所见,该解决方案与列出类的解决方案非常相似。

/src/test/java/org/junit/internal/MethodSorterTest.java
=======================================================[L 58] MethodSorter.getDeclaredMethods(clazz)[L 64] m.isSynthetic()[L 65] m.toString()[L 65] clazz.getName()[L 65] m.toString().replace(clazz.getName() + '.', "")[L 65] names.add(m.toString().replace(clazz.getName() + '.', ""))[L 74] Arrays.asList(EPSILON, BETA, ALPHA, DELTA, GAMMA_VOID, GAMMA_BOOLEAN)[L 75] getDeclaredMethodNames(DummySortWithoutAnnotation.class)[L 76] assertEquals(expected, actual)[L 81] Arrays.asList(SUPER_METHOD)[L 82] getDeclaredMethodNames(Super.class)[L 83] assertEquals(expected, actual)[L 88] Arrays.asList(SUB_METHOD)[L 89] getDeclaredMethodNames(Sub.class)[L 90] assertEquals(expected, actual)[L 118] Arrays.asList(EPSILON, BETA, ALPHA, DELTA, GAMMA_VOID, GAMMA_BOOLEAN)[L 119] getDeclaredMethodNames(DummySortWithDefault.class)[L 120] assertEquals(expected, actual)[L 148] DummySortJvm.class.getDeclaredMethods()[L 149] MethodSorter.getDeclaredMethods(DummySortJvm.class)[L 150] assertArrayEquals(fromJvmWithSynthetics, sorted)[L 178] Arrays.asList(ALPHA, BETA, DELTA, EPSILON, GAMMA_VOID, GAMMA_BOOLEAN)[L 179] getDeclaredMethodNames(DummySortWithNameAsc.class)[L 180] assertEquals(expected, actual)

下一步

您可以使用此处介绍的方法回答很多问题:浏览AST,找到您感兴趣的节点,并获取所需的任何信息。 但是,我们还需要考虑其他几件事:首先,如何转换代码。 尽管提取信息非常有用,但是重构更加有用。 然后,对于更高级的问题,我们需要使用java-symbol-solver解析符号。 例如:

  • 查看AST,我们可以找到一个类的名称,但不能找到它间接实现的接口列表
  • 在查看方法调用时,我们无法轻易找到该方法的声明。 它在哪个类或接口中声明? 我们要调用哪些不同的重载变体?

我们将在将来对此进行研究。 希望这些例子可以帮助您入门!

翻译自: https://www.javacodegeeks.com/2016/02/getting-started-javaparser-analyzing-java-code-programmatically.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GoldenGate Logdump基本使用

Logdump是GoldenGate复制软件中附带的一个工具软件&#xff0c;在OGG的目录下可以找到。这个工具主要用于分析OGG生成的队列文件&#xff0c;查找记录、统计队列文件中的数据等。 在OGG安装目录下执行logdump.exe or ./logdump即可进入命令行。 开始查找记录之前&#xff0c;先…

.bam.bai的意义_业务活动监视器(BAM)2.0带来的革命

.bam.bai的意义生产兼具精益和企业价值的中间件是一项艰巨的工作。 它要么不存在&#xff0c;要么需要创新的思维&#xff08;很多&#xff09;&#xff0c;并且需要在实现中反复进行。 业务风险很大&#xff0c;但是如果您做对了&#xff0c;它就会使您领先于其他任何公司。 这…

数据结构和算法之排序五:选择排序

我们上一篇谈到了冒泡排序&#xff0c;其实我也说了&#xff0c;这两个排序方式何其相似&#xff0c;如果掌握了冒泡排序再来进行选择排序的理解我觉得完全没有太大的问题。那么什么叫做选择排序呢&#xff1f;我们可以理解为矮子里面挑高个&#xff0c;比如说呀有一个富翁来到…

Visual Studio Code使用问题

1、打开vscode黑屏 右击vscode快捷方式–>属性–>兼容性—>兼容模式打钩 重启vscode就可以了。 2、vscode终端没有显示路径&#xff0c;不能输入 显示如下图 则关闭VS Code ,右键单击VS Code 图标&#xff0c;选择属性->兼容性&#xff0c;取消勾选 已兼容模式运…

Java社区调查结果:74%的开发人员希望减少详细程度

一个新的JDK增强建议&#xff08;JEP&#xff09;在Java社区中风起云涌&#xff1a;JEP286。该建议建议在Java的未来版本中引入局部变量类型推断&#xff0c;以简化Java应用程序的编写。 在下面的文章中&#xff0c;我们将解释它的含义以及它将如何影响您的代码。 新帖&#…

coherence安装_Oracle Coherence:分布式数据管理

coherence安装本文介绍如何使用Oracle Coherence提供分布式&#xff08;分区&#xff09;数据管理。 在下面的示例应用程序中&#xff0c;创建了一个名为OTV的新集群&#xff0c;并且在该集群的两个成员之间分配了一个名为user-map的缓存对象。 二手技术&#xff1a; JDK 1.6.…

JavaFX技巧来节省内存! 属性和可观察物的阴影场

在 JavaFX的世界中&#xff0c; Properties API允许UI开发人员将值绑定到UI控件。 这种功能非常容易&#xff0c;但是当对象模型经常使用属性时&#xff0c;应用程序可能会很快耗尽内存。 我通常会编写两个单独的对象&#xff0c;例如pojo类和表示模型对象。 此技术通常在基于S…

如何在Hibernate Search 5.5.2 / Apache Lucene 5.4.x中处理停用词?

停用词&#xff0c;例如[“ a”&#xff0c;“ an”&#xff0c;“ and”&#xff0c;“ are”&#xff0c;“ as”&#xff0c;“ at”&#xff0c;“ be”&#xff0c;“ but”&#xff0c;“ by”&#xff0c;“ for”&#xff0c;“ if”&#xff0c;“在”&#xff0c;“成…

Java----前端验证之验证码额实现

验证码是常用的登录验证方式之一,最大的作用就是保证安全,验证码的生成在java中实现的方式有很多种,比如后台生成传输到前端页面,在前台直接生成进行验证,下面写一个最简单实现验证码验证登录的例子. 生成验证码: 验证码验证逻辑: From表单登录: 没错,就这么的简单.在scripts生…

使用Spring Boot隔离集成测试和模拟依赖项

集成测试可能很慢且不可靠&#xff0c;因为它们依赖于系统中过多的组件。 在某种程度上&#xff0c;这是不可避免的&#xff1a;这里的集成测试是为了验证系统的每个部分如何与其他内部或外部组件一起玩。 但是&#xff0c;我们可以通过仅分解所需的依赖关系而不是整个系统来改…

Ubuntu 16.04下使用Wine安装Xshell 4和Xftp 4

说明&#xff1a; 1、使用的Wine版本是深度出品&#xff08;Deepin&#xff09;&#xff0c;已经精简了很多没用的配置&#xff0c;使启动能非常快&#xff0c;占用资源小。 2、由于Xshell 5的C库无法在这个Wine版本运行&#xff0c;即使升级官方原版的2版本也无法解决&#xf…

电脑缺失MSVCP110.dll文件

安装某软件显示如下错误。 错误&#xff1a; 原因&#xff1a;电脑缺少MSVCP110.dll系统文件。C:\Windows\System32目录下没有此文件。 解决&#xff1a; 下载vcredist_x64直接双击安装&#xff0c;安装完成后就可以继续安装之前安装不了的软件了。。 这里是我下载的&#…

在WildFly的REST Web服务中与Jackson的双向关系

这是使用Jackson的REST Web服务中Java实体之间的双向关系的示例。 假设我们在两个实体Parent和Child之间存在双向关系。 使用MySQL工作台为这两个表生成SQL模式文件。 DROP SCHEMA IF EXISTS bidirectional_schema ; CREATE SCHEMA IF NOT EXISTS bidirectional_schema DEFA…

Postman安装与使用(网络请求神器)--post、get请求

安装 1、Postman最早是作用chrome浏览器插件存在的&#xff0c;所以&#xff0c;你可以到chrome商店搜索下载安装&#xff0c;因为重所周知的原因&#xff0c;所以&#xff0c;大家都会找别人共享的postman插件文件来安装。由于2018年初Chrome停止对Chrome应用程序的支持。 官…

openshift用户管理_OpenShift Express Web管理控制台:入门

openshift用户管理本周&#xff0c; 最新版本的OpenShift为已经很棒的PaaS Cloud提供商带来了两个非常好的功能。 首先&#xff0c;JBoss AS已从7.0升级到7.1&#xff0c;并且所有新的Express Web Management Console已作为预览发布。 在本文中&#xff0c;我们将研究如何使用此…

linux系统搭建ftp服务器--只给某个用户访问其默认目录下的文件

1、环境: window操作系统中安装FlashFXP 软件或xftp&#xff1b; 服务器端的操作系统为centos8&#xff1b; 2、检查安装vsftpd软件 查看所有的安装的软件包 并在结果中查找包含vsftp 的文件 rpm -qa | grep vsftpd如果没有装则使用yum命令安装 yum -y install vsftpd3、创…

Openshift源中的高可用性Drools无状态服务

嗨&#xff0c;大家好&#xff01; 在这篇博客文章中&#xff0c;我想举一个简单的例子&#xff0c;展示使用Openshift 3&#xff08;Docker和Kubernetes&#xff09;扩展Drools Stateless服务有多么容易。 我将展示如何通过按需提供新实例来扩展我们的服务&#xff0c;以及如何…

jpa jsf_完整的Web应用程序Tomcat JSF Primefaces JPA Hibernate –第1部分

jpa jsf我们创建了这篇文章&#xff0c;将展示如何使用以下工具创建完整的Web应用程序&#xff1a;Tomcat7&#xff0c;带有Primefaces的JSF2&#xff08;Facelets和Libraries&#xff09;&#xff08;具有AutoComplete&#xff09;&#xff0c;JPA / Hibernate&#xff08;具有…

错题

1. 本题考查String对象的声明和赋值方式。C#中没有new String() 这种方式来构造字符串。故选 AC 2. 在CSS中&#xff0c;控制列表样式的属性有&#xff1a;list-style-type&#xff0c;list-style-position&#xff0c;list-style-image&#xff0c;list-style equals比较的是像…

javascript-发布订阅模式

说明&#xff1a;本篇文章转载自小火柴的蓝色理想的一篇博文。原文地址&#xff1a;http://www.cnblogs.com/xiaohuochai/p/8031564.html 发布—订阅模式又叫观察者模式&#xff0c;它定义对象间的一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖…