JavaParser中AST节点的观察者

我们离JavaParser 3.0的第一个候选发布版本越来越近。 我们添加的最后一项功能是支持观察抽象语法树的所有节点的更改。 当我为此功能编写代码时,我收到了Danny van Bruggen(又名Matozoid)和Cruz Maximilien的宝贵反馈。 因此,我使用“我们”来指代JavaParser团队。

AST节点上的哪些观察者可以用于?

我认为这对于JavaParser的生态系统来说是非常重要的功能,因为它通过对AST所做的更改做出反应,使与JavaParser的集成变得更加容易。 可以观察到的可能更改是为类设置新名称或添加新字段。 不同的工具可以以不同的方式对这些变化做出反应。 例如:

  • 编辑者可以更新其符号列表,该列表可用于自动完成等操作
  • 一些框架可以重新生成源代码以反映更改
  • 可以执行验证以验证新更改是否导致无效的AST
  • 像JavaSymbolSolver这样的库可以重新计算表达式的类型

这些只是我想到的一些想法,但我认为使用JavaParser的大多数方案都可以从对更改做出反应的可能性中受益。

AstObserver

JavaParser 3.0 AST基于Nodes和NodeLists。 节点(例如,如TypeDeclaration)可以具有不同的子组。 当这些组可以包含多个节点时,我们使用NodeLists。 例如,一个TypeDeclarations可以具有多个成员(字段,方法,内部类)。 因此,每个TypeDeclaration都有一个NodeList来包含字段,一个NodeList来包含方法,等等。其他子项(如TypeDeclaration的名称)则直接包含在节点中。

我们引入了一个名为AstObserver的新接口 AstObserver接收节点和NodeList上的更改。

/*** An Observer for an AST element (either a Node or a NodeList).*/
public interface AstObserver {/*** Type of change occurring on a List*/public enum ListChangeType {ADDITION,REMOVAL}/*** The value of a property is changed** @param observedNode owner of the property* @param property property changed* @param oldValue value of the property before the change* @param newValue value of the property after the change*/void propertyChange(Node observedNode, ObservableProperty property, Object oldValue, Object newValue);/*** The parent of a node is changed** @param observedNode node of which the parent is changed* @param previousParent previous parent* @param newParent new parent*/void parentChange(Node observedNode, Node previousParent, Node newParent);/*** A list is changed** @param observedNode list changed* @param type type of change* @param index position at which the changed occurred* @param nodeAddedOrRemoved element added or removed*/void listChange(NodeList observedNode, ListChangeType type, int index, Node nodeAddedOrRemoved);
}

观察什么

现在我们有了一个AstObserver ,我们需要决定应该接收哪些更改。 我们考虑了三种可能的情况:

  1. 仅观察一个节点,例如ClassDeclaration。 观察者将收到有关该节点上的更改的通知(例如,如果类更改名称),而不是其任何后代的通知。 例如,如果类更改名称的字段不会通知观察者
  2. 对于观察者注册时的节点及其所有后代。 在这种情况下,如果我注册了ClassDeclaration的观察者,则将通知我有关类及其所有字段和方法的更改。 如果添加了新字段,后来又进行了修改,那么我将不会收到有关这些更改的通知
  3. 对于一个节点及其所有后代,在观察者注册时存在的节点和以后添加的节点。

因此,节点现在具有此方法:

/*** Register a new observer for the given node. Depending on the mode specified also descendants, existing* and new, could be observed. For more details seeObserverRegistrationMode .*/public void register(AstObserver observer, ObserverRegistrationMode mode) {if (mode == null) {throw new IllegalArgumentException("Mode should be not null");}switch (mode) {case JUST_THIS_NODE:register(observer);break;case THIS_NODE_AND_EXISTING_DESCENDANTS:registerForSubtree(observer);break;case SELF_PROPAGATING:registerForSubtree(PropagatingAstObserver.transformInPropagatingObserver(observer));break;default:throw new UnsupportedOperationException("This mode is not supported: " + mode);}}

为了区分这三种情况,我们仅使用一个枚举( ObserverRegistrationMode )。 稍后,您将看到我们如何实现PropagatingAstObserver

实施对观察员的支持

如果JavaParser基于诸如EMF之类的元建模框架,则这将非常简单。 鉴于情况并非如此,我需要在AST类的所有设置器中添加一个通知调用(其中有90个左右)。

因此,在特定节点上调用setter时,它将通知所有观察者。 简单。 以TypeDeclaration <T>中的 setName 为例

@Override
public T setName(SimpleName name) {notifyPropertyChange(ObservableProperty.NAME, this.name, name);this.name = assertNotNull(name);setAsParentNodeOf(name);return (T) this;
}

给定我们没有适当的元模型,就没有属性的定义。 因此,我们在枚举中添加了一个名为ObservableProperty的属性列表。 通过这种方式,观察者可以检查更改了哪个属性并决定如何做出反应。

观察者的内部等级

出于性能原因,每个节点都有其自己的观察者列表。 当我们要观察节点的所有后代时,我们只需向该子树中的所有节点和节点列表添加相同的观察者即可。

但是,这还不够,因为在某些情况下,您可能还需要观察放置观察者后添加到子树中的所有节点。 我们通过使用PropagatingAstObserver做到这一点。 这是一个AstObserver,当看到一个新节点已附加到该节点时,它也开始观察该新节点。 简单吧?

/*** This AstObserver attach itself to all new nodes added to the nodes already observed.*/
public abstract class PropagatingAstObserver implements AstObserver {/*** Wrap a given observer to make it self-propagating. If the given observer is an instance of PropagatingAstObserver* the observer is returned without changes.*/public static PropagatingAstObserver transformInPropagatingObserver(final AstObserver observer) {if (observer instanceof PropagatingAstObserver) {return (PropagatingAstObserver)observer;}return new PropagatingAstObserver() {@Overridepublic void concretePropertyChange(Node observedNode, ObservableProperty property, Object oldValue, Object newValue) {observer.propertyChange(observedNode, property, oldValue, newValue);}@Overridepublic void concreteListChange(NodeList observedNode, ListChangeType type, int index, Node nodeAddedOrRemoved) {observer.listChange(observedNode, type, index, nodeAddedOrRemoved);}@Overridepublic void parentChange(Node observedNode, Node previousParent, Node newParent) {observer.parentChange(observedNode, previousParent, newParent);}};}@Overridepublic final void propertyChange(Node observedNode, ObservableProperty property, Object oldValue, Object newValue) {considerRemoving(oldValue);considerAdding(newValue);concretePropertyChange(observedNode, property, oldValue, newValue);}@Overridepublic final void listChange(NodeList observedNode, ListChangeType type, int index, Node nodeAddedOrRemoved) {if (type == ListChangeType.REMOVAL) {considerRemoving(nodeAddedOrRemoved);} else if (type == ListChangeType.ADDITION) {considerAdding(nodeAddedOrRemoved);}concreteListChange(observedNode, type, index, nodeAddedOrRemoved);}public void concretePropertyChange(Node observedNode, ObservableProperty property, Object oldValue, Object newValue) {// do nothing}public void concreteListChange(NodeList observedNode, ListChangeType type, int index, Node nodeAddedOrRemoved) {// do nothing}@Overridepublic void parentChange(Node observedNode, Node previousParent, Node newParent) {// do nothing}private void considerRemoving(Object element) {if (element instanceof Observable) {if (((Observable) element).isRegistered(this)) {((Observable) element).unregister(this);}}}private void considerAdding(Object element) {if (element instanceof Node) {((Node) element).registerForSubtree(this);} else if (element instanceof Observable) {((Observable) element).register(this);}}}

观察员在行动

让我们看看这在实践中如何工作:

// write some code and parse it
String code = "class A { int f; void foo(int p) { return 'z'; }}";
CompilationUnit cu = JavaParser.parse(code);// set up our observer
List changes = new ArrayList<>();
AstObserver observer = new AstObserverAdapter() {@Overridepublic void propertyChange(Node observedNode, ObservableProperty property, Object oldValue, Object newValue) {changes.add(String.format("%s.%s changed from %s to %s", observedNode.getClass().getSimpleName(), property.name().toLowerCase(), oldValue, newValue));}
};
cu.getClassByName("A").register(observer, /* Here we could use different modes */);// Doing some changes
cu.getClassByName("A").setName("MyCoolClass");
cu.getClassByName("MyCoolClass").getFieldByName("f").setElementType(new PrimitiveType(PrimitiveType.Primitive.Boolean));
cu.getClassByName("MyCoolClass").getMethodsByName("foo").get(0).getParamByName("p").setName("myParam");
// Here we are adding a new field and immediately changing it
cu.getClassByName("MyCoolClass").addField("int", "bar").getVariables().get(0).setInit("0");// If we registered our observer with mode JUST_THIS_NODE
assertEquals(Arrays.asList("ClassOrInterfaceDeclaration.name changed from A to MyCoolClass"), changes);// If we registered our observer with mode THIS_NODE_AND_EXISTING_DESCENDANTS
assertEquals(Arrays.asList("ClassOrInterfaceDeclaration.name changed from A to MyCoolClass","FieldDeclaration.element_type changed from int to boolean","VariableDeclaratorId.name changed from p to myParam"), changes);// If we registered our observer with mode SELF_PROPAGATING
assertEquals(Arrays.asList("ClassOrInterfaceDeclaration.name changed from A to MyCoolClass","FieldDeclaration.element_type changed from int to boolean","VariableDeclaratorId.name changed from p to myParam","FieldDeclaration.modifiers changed from [] to []","FieldDeclaration.element_type changed from empty to int","VariableDeclaratorId.array_bracket_pairs_after_id changed from com.github.javaparser.ast.NodeList@1 to com.github.javaparser.ast.NodeList@1","VariableDeclarator.init changed from null to 0"), changes);

结论

我对这个新功能感到非常兴奋,因为我认为它使JavaParser可以完成更多很酷的事情。 我认为我们作为提交者的工作是使其他人能够做我们目前未预见的事情。 我们应该只是充当推动者,然后躲开。

我真的很好奇,看看人们会如何发展。 顺便说一句,您知道您想让我们知道的任何使用JavaParser的项目吗? 在GitHub上发表评论或发表问题,我们期待您的来信!

翻译自: https://www.javacodegeeks.com/2016/11/observers-ast-nodes-javaparser.html

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

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

相关文章

nc命令详解

NetCat&#xff0c;在网络工具中有“瑞士军刀”美誉&#xff0c;其有Windows和Linux的版本。因为它短小精悍&#xff08;1.84版本也不过25k&#xff0c;旧版本或缩减版甚至更小&#xff09;、功能实用&#xff0c;被设计为一个简单、可靠的网络工具&#xff0c;可通过TCP或UDP协…

startindex 不能大于字符串长度_「12」学习MySQL第二类函数:字符串函数

前一篇文章总结了下MySQL中常用的数学函数&#xff0c;本文接下来的重点就是认识“字符串函数”。上图这匹漂亮的马儿就是通过各种字符(字母、括号、逗号、竖线等)画出来的。我不由自主地会去想下面几个问题&#xff1a;它到底有多少个字符&#xff1f;--字符串长度它里面的小写…

对数函数的C、MATLAB表示

1、C语言中的对数函数 头文件&#xff1a;#include格式&#xff1a;①double log (double x);log() 函数返回以 e 为底的对数值&#xff0c;即数学中的lnx&#xff1b;如果 x 为负数或 0&#xff0c;则会发生错误并设置 errno 值。错误代码&#xff1a;EDOM&#xff1a;参数x 为…

微信小程序开发——超链接或按钮点击跳转到其他页面失效

1. 超链接导航失效&#xff1a; 小程序规则——wx.navigateTo 和 wx.redirectTo 不允许跳转到 tabbar 页面&#xff0c;只能用 wx.switchTab 跳转到 tabbar 页面转载于:https://www.cnblogs.com/xyyt/p/9252835.html

javaone_JavaOne 2012:Lambda之路

javaone我最热切期待的JavaOne 2012演讲之一是Brian Goetz的“通往Lambda的道路”。 昨晚的技术主题演讲中的Lambda味道仅增加了预期。 这是在希尔顿广场A / B举行的&#xff0c;距离我之前在金门大桥A / B / C参加的演讲仅几步之遥。 我曾预计会打包相对较大的Plaza A / B&…

uml 时序图_UML各种图总结:

UML的书籍&#xff1a;《UML distilled》、《UML和模式应用》、《UML用户指南》、《UML对象设计与编程》、《UMLOracle8建模》UML&#xff08;Unified Modeling Language&#xff09;是一种统一建模语言&#xff0c;为面向对象开发系统的产品进行说明、可视化、和编制文档的一种…

让程序结果在屏幕上暂停一段时间

现在使用VS2013输出printf显示结果&#xff0c;经常会出现一闪而过的情况&#xff0c;现有以下解决办法&#xff1a; 1.调用系统命令暂停 #include <stdlib.h>system("pause");2. #include<conio.h> getchar(); 3. #include<stdlib.h> getchar(); …

Linux shell的标准输入、输出和错误

1.文件描述符 在linux shell执行命令时&#xff0c;每个进程都和三个打开的文件相联系&#xff0c;并使用文件描述符来引用这些文件。由于文件描述符不容易记忆&#xff0c;shell同时也给出了相应的文件名&#xff1a; 文件文件描述符输入文件—标准输入0&#xff08;缺省是键…

微基准测试进入Java 9

我已经几个月没有在这里写文章了&#xff0c;这种例外也会继续下去。 我计划在明年三月左右恢复写作。 本文末尾的说明。 等待&#xff01; 不完全是最后&#xff0c;因为您可以向下滚动。 它在文章结尾处。 继续阅读&#xff01; 三年前&#xff0c;我在写有关Java编译器如何…

摆渡车(noip2018 pj t3)

摆渡车&#xff08;题目和测试右转 洛谷P5017&#xff09; 做法&#xff1a;dp各种优化(剪枝) 这道题考场上看了一脸懵逼...第一眼看这 tm 不是个一维dp吗...结果按着这个朦胧的思路&#xff0c;删删改改约莫0.5h&#xff0c;终于过了小样例&#xff0c;然后一测大样例...GG了。…

关于树的冷知识

1、为什么冬天树干刷白漆&#xff1f; 刷的是是石灰水&#xff0c;主要成分是石灰乳,还有食盐,大豆粉,石榴合剂。 好处一、石灰具有一定的杀菌、杀虫作用&#xff0c;可以杀死寄生在树干上的一些越冬的真菌、细菌和害虫。 好处二、由于害虫一般都喜欢黑色、航脏的地方&#x…

乐玩自动化测试模块_自动化测试模型(一)自动化测试模型介绍

一个自动化测试框架就是一个集成体系&#xff0c;在这一体系中包含测试功能的函数库&#xff0c;测试数据源&#xff0c;测试对象标准&#xff0c;以及各种可重用的模块。自动化测试在发展过程中经历了以下几个阶段&#xff0c;模块驱动测试&#xff0c;数据驱动测试及对象驱动…

linux特殊权限位之setuid、setgid和sticky

我们登陆到系统之后,创建一个普通文件或者目录的时候,会有一个默认的权限。普通文件是644,目录文件是755,想必大家都知道这个是由umask这个值决定的。我们可以直接执行umask命令查看&#xff0c;linux系统默认的umask值是0022。想改变创建文件默认的权限&#xff0c;我们直接修…

js获取当前时间,并实时更新

可以使用JavaScript的Date()对象来获取当前时间&#xff0c;并使用setInterval()函数实现实时更新。 以下是一个示例代码&#xff1a; <p id"time"></p>function updateTime() {var now new Date();var hours now.getHours();var minutes now.getMi…

11.23日常

整理相关资料&#xff0c;阅读c#课本转载于:https://www.cnblogs.com/JL3Peanut/p/10032318.html

python requests 10041报错_pythonrequests返回unicode异常消息(或如何设置请求区域设置)...

您可以尝试os.strerror&#xff0c;但它可能不会返回任何内容或相同的非英语字符串。在ENGLISH_WINDOWS_SOCKET_MESSAGES {10004: "Interrupted function call.",10013: "Permission denied.",10014: "Bad address.",10022: "Invalid argu…

深度学习(花书)

1.英文主页http://www.deeplearningbook.org/lecture_slides.html 2.中文主页https://github.com/exacity/deeplearningbook-chinese 在线阅读https://exacity.github.io/deeplearningbook-chinese/转载于:https://www.cnblogs.com/Eufisky/p/10010046.html

表面配准论文1--基于高阶图匹配方法的稠密表面配准

Dense Non-rigid Surface Registration Using High-Order Graph Matching 一.摘要提出高阶图匹配方程来解决非刚性表面配准问题&#xff0c;单阶项描述了几何和外观相似性&#xff08;曲率和纹理&#xff09;&#xff0c;高阶项对内部嵌入能量&#xff08;intrinsic embedding …

Apache Spark软件包,从XML到JSON

Apache Spark社区为扩展Spark付出了很多努力。 最近&#xff0c;我们希望将XML数据集转换为更易于查询的内容。 我们主要对每天进行的数十亿笔交易之上的数据探索感兴趣。 XML是一种众所周知的格式&#xff0c;但是有时使用起来可能很复杂。 例如&#xff0c;在Apache Hive中&a…