eclipse中ast_JavaParser中AST节点的观察者
eclipse中ast
我們離JavaParser 3.0的第一個(gè)候選發(fā)布版本越來越近。 我們添加的最后一項(xiàng)功能是支持觀察抽象語法樹的所有節(jié)點(diǎn)的更改。 當(dāng)我為此功能編寫代碼時(shí),我收到了Danny van Bruggen(又名Matozoid)和Cruz Maximilien的寶貴反饋。 因此,我使用“我們”來指代JavaParser團(tuán)隊(duì)。
AST節(jié)點(diǎn)上的哪些觀察者可以用于?
我認(rèn)為這對于JavaParser的生態(tài)系統(tǒng)來說是非常重要的功能,因?yàn)樗ㄟ^對AST所做的更改做出React,使與JavaParser的集成變得更加容易。 可以觀察到的可能更改是為類設(shè)置新名稱或添加新字段。 不同的工具可以以不同的方式對這些變化做出React。 例如:
- 編輯者可以更新其符號列表,該列表可用于自動(dòng)完成等操作
- 一些框架可以重新生成源代碼以反映更改
- 可以執(zhí)行驗(yàn)證以驗(yàn)證新更改是否導(dǎo)致無效的AST
- JavaSymbolSolver之類的庫可以重新計(jì)算表達(dá)式的類型
這些只是我想到的一些想法,但我認(rèn)為使用JavaParser的大多數(shù)方案都可以從對更改做出React的可能性中受益。
AstObserver
JavaParser 3.0 AST基于Nodes和NodeLists。 一個(gè)節(jié)點(diǎn),例如一個(gè)TypeDeclaration ,可以具有不同的子組。 當(dāng)這些組可以包含多個(gè)節(jié)點(diǎn)時(shí),我們使用NodeLists。 例如,一個(gè)TypeDeclarations可以具有多個(gè)成員(字段,方法,內(nèi)部類)。 因此,每個(gè)TypeDeclaration都有一個(gè)NodeList來包含字段,一個(gè)NodeList來包含方法,等等。其他子項(xiàng)(如TypeDeclaration的名稱)則直接包含在一個(gè)節(jié)點(diǎn)中。
我們引入了一個(gè)名為AstObserver的新接口。 AstObserver接收節(jié)點(diǎn)和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); }觀察什么
現(xiàn)在我們有了一個(gè)AstObserver ,我們需要確定它應(yīng)該接收哪些更改。 我們考慮了三種可能的情況:
因此,Node現(xiàn)在具有此方法:
/*** 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);}}為了區(qū)分這三種情況,我們僅使用一個(gè)枚舉( ObserverRegistrationMode )。 稍后,您將看到我們?nèi)绾螌?shí)現(xiàn)PropagatingAstObserver 。
實(shí)施對觀察員的支持
如果JavaParser基于諸如EMF之類的元建模框架,則這將非常簡單。 鑒于情況并非如此,我需要在AST類的所有設(shè)置器中添加一個(gè)通知調(diào)用(其中有90個(gè)左右)。
因此,當(dāng)在特定節(jié)點(diǎn)上調(diào)用setter時(shí),它將通知所有觀察者。 簡單。 以TypeDeclaration <T>中的 setName 為例 :
@Override public T setName(SimpleName name) {notifyPropertyChange(ObservableProperty.NAME, this.name, name);this.name = assertNotNull(name);setAsParentNodeOf(name);return (T) this; }鑒于我們沒有適當(dāng)?shù)脑P?#xff0c;因此沒有屬性的定義。 因此,我們在枚舉中添加了一個(gè)名為ObservableProperty的屬性列表。 通過這種方式,觀察者可以檢查更改了哪個(gè)屬性并決定如何做出React。
觀察者的內(nèi)部等級
出于性能原因,每個(gè)節(jié)點(diǎn)都有自己的觀察者列表。 當(dāng)我們要觀察節(jié)點(diǎn)的所有后代時(shí),我們只需向該子樹中的所有節(jié)點(diǎn)和節(jié)點(diǎn)列表添加相同的觀察者即可。
但是,這還不夠,因?yàn)樵谀承┣闆r下,您可能還需要觀察放置觀察者后添加到子樹中的所有節(jié)點(diǎn)。 我們通過使用PropagatingAstObserver做到這一點(diǎn)。 這是一個(gè)AstObserver,當(dāng)看到一個(gè)新節(jié)點(diǎn)已附加到該節(jié)點(diǎn)時(shí),它也開始觀察該新節(jié)點(diǎn)。 簡單吧?
/*** 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);}}}觀察員在行動(dòng)
讓我們看看這在實(shí)踐中如何工作:
// 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);結(jié)論
我對此新功能感到非常興奮,因?yàn)槲艺J(rèn)為它使JavaParser可以完成更多很酷的事情。 我認(rèn)為我們作為提交者的工作是使其他人能夠做我們目前未預(yù)見的事情。 我們應(yīng)該只是充當(dāng)推動(dòng)者,然后躲開。
我真的很好奇,看看人們會(huì)如何發(fā)展。 順便說一句,您知道您想讓我們知道的任何使用JavaParser的項(xiàng)目嗎? 在GitHub上發(fā)表評論或打開問題,我們期待您的來信!
翻譯自: https://www.javacodegeeks.com/2016/11/observers-ast-nodes-javaparser.html
eclipse中ast
總結(jié)
以上是生活随笔為你收集整理的eclipse中ast_JavaParser中AST节点的观察者的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java基准测试_微基准测试进入Java
- 下一篇: 阿里云宣布开源通义千问 140 亿参数模