我们离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

JavaParser中AST节点的观察者相关推荐

  1. eclipse中ast_JavaParser中AST节点的观察者

    eclipse中ast 我们离JavaParser 3.0的第一个候选发布版本越来越近. 我们添加的最后一项功能是支持观察抽象语法树的所有节点的更改. 当我为此功能编写代码时,我收到了Danny va ...

  2. 什么是php的ast结构,什么是AST?Vue源码中AST语法树的解析

    这篇文章给大家介绍的内容是关于什么是AST?Vue源码中AST语法树的解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 什么是AST AST是指抽象语法树(abstract syn ...

  3. LeetCode实战:删除链表中的节点

    背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 Write a fun ...

  4. Php 获取xml中的节点值

    Php获取xml中的节点值 XML: test_xml2.xml <?xml version="1.0" encoding="UTF-8"?> &l ...

  5. zTree中父节点禁用,子节点可以用

    参考学习网址:http://www.treejs.cn/v3/main.php#_zTreeInfo zTree中父节点禁用,子节点可以用 axios.get('/base/unit/unittree ...

  6. 【Groovy】自定义 Xml 生成器 BuilderSupport ( 继承 BuilderSupport 抽象类 | 在 createNode 方法中获取节点名称、节点属性、节点值信息 )

    文章目录 一.继承 BuilderSupport 抽象类 二.在 createNode 方法中获取节点名称.节点属性.节点值信息 三.完整代码示例 1.MyBuilderSupport 生成器代码 2 ...

  7. 【Groovy】Xml 反序列化 ( 使用 XmlParser 解析 Xml 文件 | 获取 Xml 文件中的节点和属性 | 获取 Xml 文件中的节点属性 )

    文章目录 一.创建 XmlParser 解析器 二.获取 Xml 文件中的节点 三.获取 Xml 文件中的节点属性 四.完整代码示例 一.创建 XmlParser 解析器 创建 XmlParser 解 ...

  8. [Flex] 组件Tree系列 —— 阻止用户点击选中Tree中分支节点

    mxml: 1 <?xml version="1.0" encoding="utf-8"?> 2 <!--功能描述:阻止用户点击选中Tree中 ...

  9. Jquery中替换节点的方法replaceWith()和replaceAll()

    本文转自:http://www.cnblogs.com/shuang121/archive/2011/12/27/2303748.html 在jquery中,我们可以通过replaceWith()和r ...

最新文章

  1. VC manifest
  2. Codewars Vasya - Clerk--6 kyu--Python解法
  3. 1130 - Host ‘win7' is not allowed to connect to this mysql server
  4. SAP CRM和Cloud for Customer订单中的业务伙伴的自动决定机制
  5. How does ABAP check table work
  6. 无线网络受限制或无连接处理方法
  7. JZ32变形~剑指 Offer 32 - II. 从上到下打印二叉树 II
  8. GDAL中MEM格式的简单使用示例
  9. baum welch java_Baum Welch估计HMM参数实例
  10. 直播盒子源码与直播盒子APP如何对接自动发卡平台
  11. java运行环境(jre)_什么是JRE? Java运行时环境简介
  12. 工程上为什么常用3dB带宽?而不是1dB或者2dB
  13. linux中MIB与MB单位的区别
  14. 有这5类人最难成为银行的优质客户!
  15. codeforces 69A. Young Physicist
  16. 微信网页小游戏网站源码带后台+可后台添加游戏+推荐到微信
  17. 团队作业1——团队展示
  18. Java面试基础知识II
  19. 正版免费图片编辑处理软件下载_图片处理软件
  20. iphone 开发之过滤html标签

热门文章

  1. JVM调优总结(2):基本垃圾回收算法
  2. 使用Java 8 Stream像操作SQL一样处理数据(上)
  3. centos7安装最新版node
  4. 用正则判断字符串是否为中文的方法
  5. Safari浏览器不支持……
  6. 实体类?Dao接口?Mapper映射文件?都别写了!!!用这种方法就可以
  7. MySQL多对多创建表语句(防忘记)
  8. c语言程序设计的一般错误的是,《C语言程序设计》第十章 程序常见错误分析.pdf...
  9. java安装显示更改_java 安装与配置
  10. 服务器 ha linux,Linux 高可用(HA)集群之Heartbeat详解