Skip to content

Commit

Permalink
Implemented findAncestor(Class,Predicate)
Browse files Browse the repository at this point in the history
  • Loading branch information
malteskoruppa committed Sep 28, 2018
1 parent dabbd31 commit 3c4c58c
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 11 deletions.
@@ -0,0 +1,83 @@
package com.github.javaparser.ast;

import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.TryStmt;
import org.junit.Test;

import static com.github.javaparser.JavaParser.parse;
import static org.junit.Assert.assertEquals;

/**
* Some tests for finding descendant and ancestor nodes.
*/
public class FindNodeTest {
@Test
public void testFindFirst() {
CompilationUnit cu = parse(
"class Foo {\n" +
" void foo() {\n" +
" try {\n" +
" } catch (Exception e) {\n" +
" } finally {\n" +
" try {\n" +
" } catch (Exception e) {\n" +
" foo();\n" +
" } finally {\n" +
" }\n" +
" }\n" +
"\n" +
" }\n" +
"}\n");

// find the method call expression foo()
MethodCallExpr actual = cu.findFirst(MethodCallExpr.class).orElse(null);

MethodCallExpr expected = cu.getType(0).getMember(0)
.asMethodDeclaration().getBody().get().getStatement(0)
.asTryStmt().getFinallyBlock().get().getStatement(0)
.asTryStmt().getCatchClauses().get(0).getBody().getStatement(0)
.asExpressionStmt().getExpression()
.asMethodCallExpr();

assertEquals(expected, actual);
}

@Test
public void testFindAncestralFinallyBlock() {
CompilationUnit cu = parse(
"class Foo {\n" +
" void foo() {\n" +
" try {\n" +
" } catch (Exception e) {\n" +
" } finally {\n" +
" try {\n" +
" } catch (Exception e) {\n" +
" foo();\n" +
" } finally {\n" +
" }\n" +
" }\n" +
"\n" +
" }\n" +
"}\n");

// find the method call expression foo()
MethodCallExpr methodCallExpr = cu.findFirst(MethodCallExpr.class).orElse(null);

// find the finally block that the method call expression foo() is in
BlockStmt actual = methodCallExpr.findAncestor(BlockStmt.class, bs -> {
if (bs.getParentNode().isPresent() && bs.getParentNode().get() instanceof TryStmt) {
TryStmt ancestralTryStmt = (TryStmt) bs.getParentNode().get();
return bs == ancestralTryStmt.getFinallyBlock().orElse(null);
}
return false;
}).orElse(null);

BlockStmt expected = cu.getType(0).getMember(0)
.asMethodDeclaration().getBody().get().getStatement(0)
.asTryStmt().getFinallyBlock().get();

assertEquals(expected, actual);
}
}

Expand Up @@ -25,55 +25,69 @@
import com.github.javaparser.ast.observer.Observable;

import java.util.Optional;
import java.util.function.Predicate;

/**
* An object that has a parent node.
* An object that can have a parent node.
*/
public interface HasParentNode<T> extends Observable {

/**
* Return the parent node or null, if no parent is set.
* Returns the parent node, or {@code null} if no parent is set.
*/
Optional<Node> getParentNode();

/**
* Set the parent node.
* Sets the parent node.
*
* @param parentNode the parent node or null, to set no parent
* @return return <i>this</i>
* @param parentNode the parent node, or {@code null} to set no parent.
* @return return {@code this}
*/
T setParentNode(Node parentNode);

/**
* <i>this</i> for everything except NodeLists. NodeLists use their parent as their children parent.
* Returns the parent node from the perspective of the children of this node.
* <p>
* That is, this method returns {@code this} for everything except {@code NodeList}. A {@code NodeList} returns its
* parent node instead. This is because a {@code NodeList} sets the parent of all its children to its own parent
* node (see {@link com.github.javaparser.ast.NodeList} for details).
*/
Node getParentNodeForChildren();

/**
* @deprecated use findAncestor
* @deprecated use {@link #findAncestor(Class)}.
*/
@Deprecated
default <N> Optional<N> getAncestorOfType(Class<N> classType) {
return findAncestor(classType);
}

/**
* @deprecated use findAncestor
* @deprecated use {@link #findAncestor(Class)}.
*/
@Deprecated
default <N> Optional<N> findParent(Class<N> type) {
return findAncestor(type);
}

/**
* Walks the parents of this node, returning the first node of type "type" or empty() if none is found.
* Also works for other types, like the With... interfaces.
* Walks the parents of this node and returns the first node of type {@code type}, or {@code empty()} if none is
* found. The given type may also be an interface type, such as one of the {@code NodeWith...} interface types.
*/
default <N> Optional<N> findAncestor(Class<N> type) {
return findAncestor(type, x -> true);
}

/**
* Walks the parents of this node and returns the first node of type {@code type} that matches {@code predicate}, or
* {@code empty()} if none is found. The given type may also be an interface type, such as one of the
* {@code NodeWith...} interface types.
*/
default <N> Optional<N> findAncestor(Class<N> type, Predicate<N> predicate) {
Optional<Node> possibleParent = getParentNode();
while (possibleParent.isPresent()) {
Node parent = possibleParent.get();
if (type.isAssignableFrom(parent.getClass())) {
if (type.isAssignableFrom(parent.getClass()) && predicate.test(type.cast(parent))) {
return Optional.of(type.cast(parent));
}
possibleParent = parent.getParentNode();
Expand Down

0 comments on commit 3c4c58c

Please sign in to comment.