PositionUtilsTest.java
/*
* Copyright (C) 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.utils;
import static com.github.javaparser.utils.PositionUtils.nodeContains;
import static com.github.javaparser.utils.TestUtils.assertEqualsStringIgnoringEol;
import static org.junit.jupiter.api.Assertions.*;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.type.Type;
import org.junit.jupiter.api.Test;
public class PositionUtilsTest {
@Test
public void nodeContains_NoAnnotationsAnywhere_IgnoringAnnotations() {
CompilationUnit cu = StaticJavaParser.parse("class X { int a; }");
FieldDeclaration field = cu.findFirst(FieldDeclaration.class).get();
boolean contains = nodeContains(cu, field, true);
assertTrue(contains);
}
@Test
public void nodeDoesNotContain_NoAnnotationsAnywhere_IgnoringAnnotations() {
CompilationUnit cu = StaticJavaParser.parse("class X { int a; }");
FieldDeclaration field = cu.findFirst(FieldDeclaration.class).get();
Type fieldType = field.getVariable(0).getType();
SimpleName fieldName = field.getVariable(0).getName();
boolean contains = nodeContains(fieldType, fieldName, true);
assertFalse(contains);
}
@Test
public void nodeContains_NoAnnotationsAnywhere_IncludeAnnotations() {
CompilationUnit cu = StaticJavaParser.parse("class X { int a; }");
FieldDeclaration field = cu.findFirst(FieldDeclaration.class).get();
boolean contains = nodeContains(cu, field, false);
assertTrue(contains);
}
@Test
public void nodeDoesNotContain_NoAnnotationsAnywhere_IncludeAnnotations() {
CompilationUnit cu = StaticJavaParser.parse("class X { int a; }");
FieldDeclaration field = cu.findFirst(FieldDeclaration.class).get();
Type fieldType = field.getVariable(0).getType();
SimpleName fieldName = field.getVariable(0).getName();
boolean contains = nodeContains(fieldType, fieldName, false);
assertFalse(contains, "Type and Name are separate branches of the AST, thus should not contain each other.");
}
@Test
public void nodeContainsAnnotations_IgnoringAnnotations() {
CompilationUnit cu = StaticJavaParser.parse("@A class X {} class Y {}");
ClassOrInterfaceDeclaration x = cu.getClassByName("X").get();
ClassOrInterfaceDeclaration y = cu.getClassByName("Y").get();
boolean contains = nodeContains(x, y, true);
assertFalse(contains);
}
@Test
public void nodeContainsAnnotations_WithCommentNodeInTheMiddle_IgnoringAnnotations() {
String code = "" + "@A\n" + "/*o*/\n" + "@B\n" + "class X {\n" + "}\n" + "";
CompilationUnit cu = StaticJavaParser.parse(code);
assertEqualsStringIgnoringEol(code, cu.toString(), "Issue with the parsing of the code, not this test.");
ClassOrInterfaceDeclaration x = cu.getClassByName("X").get();
AnnotationExpr annotationB = x.getAnnotationByName("B").get();
// Comment gets added to the MarkerAnnotationExpr for @B -- correct
Comment o = annotationB.getComment().get();
assertEquals(annotationB, o.getCommentedNode().get(), "Comment has been added to an unexpected node.");
boolean contains = nodeContains(x, o, true);
assertFalse(contains);
}
@Test
public void nodeContainsAnnotations_WithAnnotationNodeInTheMiddle() {
String code = "" + "@A\n" + "@B\n" + "@C\n" + "class X {\n" + "}\n" + "";
CompilationUnit cu = StaticJavaParser.parse(code);
assertEqualsStringIgnoringEol(code, cu.toString(), "Issue with the parsing of the code, not this test.");
final ClassOrInterfaceDeclaration x = cu.getClassByName("X").get();
final AnnotationExpr annotationA = x.getAnnotationByName("A").get();
final AnnotationExpr annotationB = x.getAnnotationByName("B").get();
final AnnotationExpr annotationC = x.getAnnotationByName("C").get();
// If including annotations (i.e. NOT ignoring them), all nodes should be included
assertTrue(nodeContains(x, annotationA, false), formatRangeCompareResult(x, annotationA, "X", "A"));
assertTrue(nodeContains(x, annotationB, false), formatRangeCompareResult(x, annotationB, "X", "B"));
assertTrue(nodeContains(x, annotationC, false), formatRangeCompareResult(x, annotationC, "X", "C"));
assertTrue(nodeContains(x, x, false), formatRangeCompareResult(x, x, "X", "X"));
// If ignoring annotations, only the node itself should be included
assertFalse(nodeContains(x, annotationA, true), formatRangeCompareResult(x, annotationA, "X", "A"));
assertFalse(nodeContains(x, annotationB, true), formatRangeCompareResult(x, annotationB, "X", "B"));
assertFalse(nodeContains(x, annotationC, true), formatRangeCompareResult(x, annotationC, "X", "C"));
assertFalse(nodeContains(x, x, true), formatRangeCompareResult(x, x, "X", "X"));
}
private String formatRangeCompareResult(Node x, Node annotationA, String containerId, String otherId) {
return String.format(
"container range in detected as NOT containing other range: " + "\n - container (%s): %s"
+ "\n - other (%s): %s",
containerId,
x.getRange().get().toString(),
otherId,
annotationA.getRange().get().toString());
}
@Test
public void nodeContainsAnnotations_WithCommentAtTheEndOfAnnotations_IgnoringAnnotations() {
CompilationUnit cu = StaticJavaParser.parse("@A @B /*o*/ public class X {}");
ClassOrInterfaceDeclaration x = cu.getClassByName("X").get();
// TODO: Should the comment be attached to the SimpleName (as opposed to the ClassOrInterfaceDeclaration?)
SimpleName simpleName = x.getName();
Comment o = simpleName.getComment().get();
//// 0 1 2 2
//// 123456789012345678901234567890
//// @A @B /*o*/ public class X {}
//// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // range of x, WITH annotations -- thus contained == TRUE
//// @A @B /*o*/ public class X {}
//// ^^^^^^^^^^^^^^^^^ // range of x, ignoring annotations -- thus contained == FALSE
//// @A @B /*o*/ public class X {}
//// ^^^^^ // range of o
// TODO: Determine if comments outside the text range of a node are "contained" within a node (part of the
// subtree, but are printed before).
assertTrue(nodeContains(x, o, false), formatRangeCompareResult(x, o, "X", "o"));
assertFalse(nodeContains(x, o, true), formatRangeCompareResult(x, o, "X", "o"));
// FIXME: Both tests currently fail due to the comment being attached to the SimpleName, as opposed to the
// ClassOrInterfaceDeclaration
// assertEquals(x.getClass(), o.getCommentedNode().get().getClass(), "Comment attached to an unexpected
// node -- expected to be the ClassOrInterfaceDeclaration");
// assertEquals(x, o.getCommentedNode().get(), "Comment attached to an unexpected node -- expected to be
// the ClassOrInterfaceDeclaration");
}
@Test
public void nodeContainsAnnotations_WithCommentAfterTheEnd_IgnoringAnnotations() {
CompilationUnit cu = StaticJavaParser.parse("@A @B public /*o*/ class X {}");
ClassOrInterfaceDeclaration x = cu.getClassByName("X").get();
// TODO: Should the comment be attached to the SimpleName (as opposed to the ClassOrInterfaceDeclaration?)
SimpleName simpleName = x.getName();
Comment o = simpleName.getComment().get();
//// 0 1 2 2
//// 123456789012345678901234567890
//// @A @B public /*o*/ class X {}
//// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // range of x, WITH annotations -- thus contained == TRUE
//// @A @B public /*o*/ class X {}
//// ^^^^^^^^^^^^^^^^^^^^^^^ // range of x, ignoring annotations -- thus contained == TRUE
//// @A @B public /*o*/ class X {}
//// ^^^^^ // range of o
// TODO: Determine if comments outside the text range of a node are "contained" within a node (part of the
// subtree, but are printed before).
assertTrue(nodeContains(x, o, false), formatRangeCompareResult(x, o, "X", "o"));
assertTrue(nodeContains(x, o, true), formatRangeCompareResult(x, o, "X", "o"));
// FIXME: Both tests currently fail due to the comment being attached to the SimpleName, as opposed to the
// ClassOrInterfaceDeclaration
// assertEquals(x.getClass(), o.getCommentedNode().get().getClass(), "Comment attached to an unexpected
// node -- expected to be the ClassOrInterfaceDeclaration");
// assertEquals(x, o.getCommentedNode().get(), "Comment attached to an unexpected node -- expected to be
// the ClassOrInterfaceDeclaration");
}
@Test
public void nodeContainsAnnotations_WithCommentAfterTheEnd_IgnoringAnnotations2() {
CompilationUnit cu = StaticJavaParser.parse("@A @B public class /*o*/ X {}");
ClassOrInterfaceDeclaration x = cu.getClassByName("X").get();
// TODO: Should the comment be attached to the SimpleName (as opposed to the ClassOrInterfaceDeclaration?)
SimpleName simpleName = x.getName();
Comment o = simpleName.getComment().get();
//// 12345678912345678912345678901
//// @A @B public class /*o*/ X {}
//// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // range of x, WITH annotations -- thus contained == TRUE
//// @A @B public class /*o*/ X {}
//// ^^^^^^^^^^^^^^^^^^^^^^^ // range of x, ignoring annotations -- thus contained == TRUE
//// @A @B public class /*o*/ X {}
//// ^^^^^ // range of o
// TODO: Determine if comments outside the text range of a node are "contained" within a node (part of the
// subtree, but are printed before).
assertTrue(nodeContains(x, o, false), formatRangeCompareResult(x, o, "X", "o"));
assertTrue(nodeContains(x, o, true), formatRangeCompareResult(x, o, "X", "o"));
assertTrue(o.getCommentedNode().isPresent());
// FIXME: Both tests currently fail due to the comment being attached to the SimpleName, as opposed to the
// ClassOrInterfaceDeclaration
// assertEquals(x.getClass(), o.getCommentedNode().get().getClass(), "Comment attached to an unexpected
// node -- expected to be the ClassOrInterfaceDeclaration");
// assertEquals(x, o.getCommentedNode().get(), "Comment attached to an unexpected node -- expected to be
// the ClassOrInterfaceDeclaration");
}
@Test
public void nodeContainsAnnotations_WithCommentAfterTheEnd_IgnoringAnnotations3() {
CompilationUnit cu = StaticJavaParser.parse("@A @B public class X /*o*/ {}");
ClassOrInterfaceDeclaration x = cu.getClassByName("X").get();
// // TODO: At what point is the declaration supposed to end and the code block begin? Should the block
// comment move "inside" the code block?
// // cu =
// @A
// @B
// public class X {
// /*o*/
// }
// TODO: Should the comment be attached to the SimpleName (as opposed to being attached to null but not
// orphaned?)
Comment o = cu.getComments().get(0);
//// 12345678912345678912345678901
//// @A @B public class X /*o*/ {}
//// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // range of x, WITH annotations -- thus contained == TRUE
//// @A @B public class X /*o*/ {}
//// ^^^^^^^^^^^^^^^^^^^^^^^ // range of x, ignoring annotations -- thus contained == TRUE
//// @A @B public class X /*o*/ {}
//// ^^^^^ // range of o
// TODO: Determine if comments outside the text range of a node are "contained" within a node (part of the
// subtree, but are printed before).
assertTrue(nodeContains(x, o, false), formatRangeCompareResult(x, o, "X", "o"));
assertTrue(nodeContains(x, o, true), formatRangeCompareResult(x, o, "X", "o"));
// FIXME: Comment is unattached (returns null), but is not considered to be orphaned...?
// assertTrue(o.getCommentedNode().isPresent());
// FIXME: Both tests currently fail due to the comment being unattached, as opposed to the
// ClassOrInterfaceDeclaration
// assertEquals(x.getClass(), o.getCommentedNode().get().getClass(), "Comment attached to an unexpected
// node -- expected to be the ClassOrInterfaceDeclaration");
// assertEquals(x, o.getCommentedNode().get(), "Comment attached to an unexpected node -- expected to be
// the ClassOrInterfaceDeclaration");
}
}