NodeTest.java
/*
* Copyright (C) 2007-2010 J��lio Vilmar Gesser.
* Copyright (C) 2011, 2013-2024 The JavaParser Team.
*
* This file is part of JavaParser.
*
* JavaParser can be used either under the terms of
* a) the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* b) the terms of the Apache License
*
* You should have received a copy of both licenses in LICENCE.LGPL and
* LICENCE.APACHE. Please refer to those files for details.
*
* JavaParser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*/
package com.github.javaparser.ast;
import static com.github.javaparser.StaticJavaParser.parse;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.github.javaparser.Position;
import com.github.javaparser.Range;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.comments.BlockComment;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.comments.JavadocComment;
import com.github.javaparser.ast.comments.LineComment;
import com.github.javaparser.ast.expr.Name;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.type.PrimitiveType;
import com.github.javaparser.utils.LineSeparator;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
class NodeTest {
@Test
void removeOrphanCommentPositiveCase() {
ClassOrInterfaceDeclaration decl = new ClassOrInterfaceDeclaration(new NodeList<>(), false, "A");
Comment c = new LineComment("A comment");
decl.addOrphanComment(c);
assertEquals(1, decl.getOrphanComments().size());
assertSame(decl, c.getParentNode().get());
assertTrue(decl.removeOrphanComment(c));
assertEquals(0, decl.getOrphanComments().size());
assertFalse(c.getParentNode().isPresent());
}
@Test
void removeOrphanCommentNegativeCase() {
ClassOrInterfaceDeclaration aClass = new ClassOrInterfaceDeclaration(new NodeList<>(), false, "A");
FieldDeclaration aField =
new FieldDeclaration(new NodeList<>(), new VariableDeclarator(PrimitiveType.intType(), "f"));
aClass.getMembers().add(aField);
Comment c = new LineComment("A comment");
aField.addOrphanComment(c);
// the comment is an orphan comment of the field, so trying to remove it on the class should not work
assertFalse(aClass.removeOrphanComment(c));
assertEquals(1, aField.getOrphanComments().size());
assertTrue(c.getParentNode().isPresent());
}
@Test
void hasJavaDocCommentPositiveCaseWithSetJavaDocComment() {
ClassOrInterfaceDeclaration decl = new ClassOrInterfaceDeclaration(new NodeList<>(), false, "Foo");
decl.setJavadocComment("A comment");
assertTrue(decl.hasJavaDocComment());
}
@Test
void hasJavaDocCommentPositiveCaseWithSetComment() {
ClassOrInterfaceDeclaration decl = new ClassOrInterfaceDeclaration(new NodeList<>(), false, "Foo");
decl.setComment(new JavadocComment("A comment"));
assertTrue(decl.hasJavaDocComment());
}
@Test
void hasJavaDocCommentNegativeCaseNoComment() {
ClassOrInterfaceDeclaration decl = new ClassOrInterfaceDeclaration(new NodeList<>(), false, "Foo");
assertFalse(decl.hasJavaDocComment());
}
@Test
void hasJavaDocCommentNegativeCaseLineComment() {
ClassOrInterfaceDeclaration decl = new ClassOrInterfaceDeclaration(new NodeList<>(), false, "Foo");
decl.setComment(new LineComment("foo"));
assertFalse(decl.hasJavaDocComment());
}
@Test
void hasJavaDocCommentNegativeCaseBlockComment() {
ClassOrInterfaceDeclaration decl = new ClassOrInterfaceDeclaration(new NodeList<>(), false, "Foo");
decl.setComment(new BlockComment("foo"));
assertFalse(decl.hasJavaDocComment());
}
@Test
void findCompilationUnitOfCommentNode() {
CompilationUnit cu = parse(
"class X {\n" + " void x() {\n" + " // this is a comment\n" + " foo();\n" + " }\n" + "}\n");
Comment comment = cu.getType(0)
.getMember(0)
.asMethodDeclaration()
.getBody()
.get()
.getStatement(0)
.getComment()
.get();
assertTrue(comment.findCompilationUnit().isPresent());
}
@Test
void findCompilationUnitOfOrphanCommentNode() {
CompilationUnit cu = parse("class X {\n" + " void x() {\n" + " // this is a comment\n" + " }\n" + "}\n");
Comment comment = cu.getType(0)
.getMember(0)
.asMethodDeclaration()
.getBody()
.get()
.getOrphanComments()
.get(0);
assertTrue(comment.findCompilationUnit().isPresent());
}
@Test
void removeAllOnRequiredProperty() {
CompilationUnit cu = parse("class X{ void x(){}}");
MethodDeclaration methodDeclaration = cu.getType(0).getMethods().get(0);
methodDeclaration.getName().removeForced();
// Name is required, so to remove it the whole method is removed.
assertEquals(String.format("class X {%1$s}%1$s", LineSeparator.SYSTEM), cu.toString());
}
@Test
void removingTheSecondOfAListOfIdenticalStatementsDoesNotMessUpTheParents() {
CompilationUnit unit = parse(String.format(
"public class Example {%1$s" + " public static void example() {%1$s"
+ " boolean swapped;%1$s"
+ " swapped=false;%1$s"
+ " swapped=false;%1$s"
+ " }%1$s"
+ "}%1$s",
LineSeparator.SYSTEM));
// remove the second swapped=false
ExpressionStmt target = unit.findAll(ExpressionStmt.class).get(2);
target.remove();
// This will throw an exception if the parents are bad.
unit.toString();
}
@Test
void findNodeByRange() {
CompilationUnit cu = parse("class X {\n" + " void x() {\n" + " }\n" + "}\n");
ClassOrInterfaceDeclaration coid =
cu.findFirst(ClassOrInterfaceDeclaration.class).get();
MethodDeclaration md = cu.findFirst(MethodDeclaration.class).get();
// The range corresponds to the compilation unit
Optional<Node> node = cu.findByRange(new Range(new Position(1, 1), new Position(4, 2)));
assertTrue(node.isPresent());
assertEquals(cu, node.get());
// The range corresponds to the class declaration
node = cu.findByRange(new Range(new Position(1, 1), new Position(4, 1)));
assertTrue(node.isPresent());
assertEquals(coid, node.get());
// The range corresponds to the method declaration
node = cu.findByRange(new Range(new Position(2, 3), new Position(3, 3)));
assertTrue(node.isPresent());
assertEquals(md, node.get());
// The range is included in the class declaration
node = cu.findByRange(new Range(new Position(1, 1), new Position(1, 1)));
assertTrue(node.isPresent());
assertEquals(coid, node.get());
// The range is not included in the compilation unit
node = cu.findByRange(new Range(new Position(5, 1), new Position(5, 1)));
assertFalse(node.isPresent());
// Search based on the method declaration, but the range corresponds to a parent node.
node = md.findByRange(new Range(new Position(1, 1), new Position(1, 1)));
assertFalse(node.isPresent());
}
@Nested
class PreOrderIteratorTest {
@Test
void rootHasNoChild() {
Node root = new CompilationUnit();
List<Node> nodes = root.findAll(Node.class, Node.TreeTraversal.PREORDER);
assertEquals(Arrays.asList(CompilationUnit.class), classesOf(nodes));
}
@Test
void rootHasChild() {
Node root = new CompilationUnit("com");
List<Node> nodes = root.findAll(Node.class, Node.TreeTraversal.PREORDER);
assertEquals(Arrays.asList(CompilationUnit.class, PackageDeclaration.class, Name.class), classesOf(nodes));
}
@Test
void astHasMultipleLeafs() {
Node root = parse("package com;" + "import com.*;" + "import org.*;" + "abstract class Foo {}");
List<Node> nodes = root.findAll(Node.class, Node.TreeTraversal.PREORDER);
assertEquals(
Arrays.asList(
CompilationUnit.class,
PackageDeclaration.class,
Name.class,
ImportDeclaration.class,
Name.class,
ImportDeclaration.class,
Name.class,
ClassOrInterfaceDeclaration.class,
Modifier.class,
SimpleName.class),
classesOf(nodes));
}
private List<Class<? extends Node>> classesOf(List<Node> nodes) {
return nodes.stream().map(Node::getClass).collect(Collectors.toList());
}
}
@Nested
class PostOrderIteratorTest {
@Test
void rootHasNoChild() {
Node root = new CompilationUnit();
List<Node> nodes = root.findAll(Node.class, Node.TreeTraversal.POSTORDER);
assertEquals(Arrays.asList(CompilationUnit.class), classesOf(nodes));
}
@Test
void rootHasChild() {
Node root = new CompilationUnit("com");
List<Node> nodes = root.findAll(Node.class, Node.TreeTraversal.POSTORDER);
assertEquals(Arrays.asList(Name.class, PackageDeclaration.class, CompilationUnit.class), classesOf(nodes));
}
@Test
void astHasMultipleLeafs() {
Node root = parse("package com;" + "import com.*;" + "import org.*;" + "abstract class Foo {}");
List<Node> nodes = root.findAll(Node.class, Node.TreeTraversal.POSTORDER);
assertEquals(
Arrays.asList(
Name.class,
PackageDeclaration.class,
Name.class,
ImportDeclaration.class,
Name.class,
ImportDeclaration.class,
Modifier.class,
SimpleName.class,
ClassOrInterfaceDeclaration.class,
CompilationUnit.class),
classesOf(nodes));
}
private List<Class<? extends Node>> classesOf(List<Node> nodes) {
return nodes.stream().map(Node::getClass).collect(Collectors.toList());
}
}
}