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 ,我們需要決定應該接收哪些更改。 我們考慮了三種可能的情況:
因此,節點現在具有此方法:
/*** 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节点的观察者的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle Service Bus简介
- 下一篇: 靠谱助手模拟器电脑版(模拟器辅助)