Home | History | Annotate | Download | only in lexicalpreservation
      1 package com.github.javaparser.printer.lexicalpreservation;
      2 
      3 import com.github.javaparser.*;
      4 import com.github.javaparser.ast.*;
      5 import com.github.javaparser.ast.body.*;
      6 import com.github.javaparser.ast.expr.*;
      7 import com.github.javaparser.ast.stmt.*;
      8 import com.github.javaparser.ast.type.Type;
      9 import com.github.javaparser.ast.type.UnionType;
     10 import com.github.javaparser.ast.type.VoidType;
     11 import com.github.javaparser.ast.visitor.ModifierVisitor;
     12 import com.github.javaparser.ast.visitor.Visitable;
     13 import org.junit.Test;
     14 
     15 import java.io.IOException;
     16 import java.util.Arrays;
     17 import java.util.List;
     18 import java.util.stream.Collectors;
     19 
     20 import static com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter.NODE_TEXT_DATA;
     21 import static com.github.javaparser.utils.TestUtils.assertEqualsNoEol;
     22 import static com.github.javaparser.utils.Utils.EOL;
     23 import static org.junit.Assert.assertEquals;
     24 import static org.junit.Assert.assertTrue;
     25 
     26 public class LexicalPreservingPrinterTest extends AbstractLexicalPreservingTest {
     27     private NodeText getTextForNode(Node node) {
     28         return node.getData(NODE_TEXT_DATA);
     29     }
     30 
     31 
     32     //
     33     // Tests on TextNode definition
     34     //
     35 
     36     @Test
     37     public void checkNodeTextCreatedForSimplestClass() {
     38         considerCode("class A {}");
     39 
     40         // CU
     41         assertEquals(1, getTextForNode(cu).numberOfElements());
     42         assertEquals(true, getTextForNode(cu).getTextElement(0) instanceof ChildTextElement);
     43         assertEquals(cu.getClassByName("A").get(), ((ChildTextElement)getTextForNode(cu).getTextElement(0)).getChild());
     44 
     45         // Class
     46         ClassOrInterfaceDeclaration classA = cu.getClassByName("A").get();
     47         assertEquals(7, getTextForNode(classA).numberOfElements());
     48         assertEquals("class", getTextForNode(classA).getTextElement(0).expand());
     49         assertEquals(" ", getTextForNode(classA).getTextElement(1).expand());
     50         assertEquals("A", getTextForNode(classA).getTextElement(2).expand());
     51         assertEquals(" ", getTextForNode(classA).getTextElement(3).expand());
     52         assertEquals("{", getTextForNode(classA).getTextElement(4).expand());
     53         assertEquals("}", getTextForNode(classA).getTextElement(5).expand());
     54         assertEquals("", getTextForNode(classA).getTextElement(6).expand());
     55         assertEquals(true, getTextForNode(classA).getTextElement(6) instanceof TokenTextElement);
     56         assertEquals(GeneratedJavaParserConstants.EOF, ((TokenTextElement)getTextForNode(classA).getTextElement(6)).getTokenKind());
     57     }
     58 
     59     @Test
     60     public void checkNodeTextCreatedForField() {
     61         String code = "class A {int i;}";
     62         considerCode(code);
     63 
     64         ClassOrInterfaceDeclaration classA = cu.getClassByName("A").get();
     65         FieldDeclaration fd = classA.getFieldByName("i").get();
     66         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(fd);
     67         assertEquals(Arrays.asList("int", " ", "i", ";"),
     68                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
     69     }
     70 
     71     @Test
     72     public void checkNodeTextCreatedForVariableDeclarator() {
     73         String code = "class A {int i;}";
     74         considerCode(code);
     75 
     76         ClassOrInterfaceDeclaration classA = cu.getClassByName("A").get();
     77         FieldDeclaration fd = classA.getFieldByName("i").get();
     78         VariableDeclarator vd = fd.getVariables().get(0);
     79         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(vd);
     80         assertEquals(Arrays.asList("i"),
     81                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
     82     }
     83 
     84     @Test
     85     public void checkNodeTextCreatedForMethod() {
     86         String code = "class A {void foo(int p1, float p2) { }}";
     87         considerCode(code);
     88 
     89         ClassOrInterfaceDeclaration classA = cu.getClassByName("A").get();
     90         MethodDeclaration md = classA.getMethodsByName("foo").get(0);
     91         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(md);
     92         assertEquals(Arrays.asList("void", " ", "foo", "(", "int p1", ",", " ", "float p2", ")", " ", "{ }"),
     93                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
     94     }
     95 
     96     @Test
     97     public void checkNodeTextCreatedForMethodParameter() {
     98         String code = "class A {void foo(int p1, float p2) { }}";
     99         considerCode(code);
    100 
    101         ClassOrInterfaceDeclaration classA = cu.getClassByName("A").get();
    102         MethodDeclaration md = classA.getMethodsByName("foo").get(0);
    103         Parameter p1 = md.getParameterByName("p1").get();
    104         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(p1);
    105         assertEquals(Arrays.asList("int", " ", "p1"),
    106                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    107     }
    108 
    109     @Test
    110     public void checkNodeTextCreatedForPrimitiveType() {
    111         String code = "class A {void foo(int p1, float p2) { }}";
    112         considerCode(code);
    113 
    114         ClassOrInterfaceDeclaration classA = cu.getClassByName("A").get();
    115         MethodDeclaration md = classA.getMethodsByName("foo").get(0);
    116         Parameter p1 = md.getParameterByName("p1").get();
    117         Type t = p1.getType();
    118         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(t);
    119         assertEquals(Arrays.asList("int"),
    120                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    121     }
    122 
    123     @Test
    124     public void checkNodeTextCreatedForSimpleImport() {
    125         String code = "import a.b.c.D;";
    126         considerCode(code);
    127 
    128         ImportDeclaration imp = (ImportDeclaration)cu.getChildNodes().get(0);
    129         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(imp);
    130         assertEquals(Arrays.asList("import", " ", "a.b.c.D", ";", ""),
    131                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    132     }
    133 
    134     @Test
    135     public void checkNodeTextCreatedGenericType() {
    136         String code = "class A {ParseResult<T> result;}";
    137         considerCode(code);
    138 
    139         FieldDeclaration field = cu.getClassByName("A").get().getFieldByName("result").get();
    140         Node t = field.getCommonType();
    141         Node t2 = field.getVariable(0).getType();
    142         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(field);
    143         assertEquals(Arrays.asList("ParseResult", "<", "T", ">", " ", "result", ";"),
    144                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    145     }
    146 
    147     @Test
    148     public void checkNodeTextCreatedAnnotationDeclaration() {
    149         String code = "public @interface ClassPreamble { String author(); }";
    150         considerCode(code);
    151 
    152         AnnotationDeclaration ad = cu.getAnnotationDeclarationByName("ClassPreamble").get();
    153         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(ad);
    154         assertEquals(Arrays.asList("public", " ", "@", "interface", " ", "ClassPreamble", " ", "{", " ", "String author();", " ", "}", ""),
    155                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    156     }
    157 
    158     @Test
    159     public void checkNodeTextCreatedAnnotationMemberDeclaration() {
    160         String code = "public @interface ClassPreamble { String author(); }";
    161         considerCode(code);
    162 
    163         AnnotationDeclaration ad = cu.getAnnotationDeclarationByName("ClassPreamble").get();
    164         AnnotationMemberDeclaration md = (AnnotationMemberDeclaration)ad.getMember(0);
    165         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(md);
    166         assertEquals(Arrays.asList("String", " ", "author", "(", ")", ";"),
    167                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    168     }
    169 
    170     @Test
    171     public void checkNodeTextCreatedAnnotationMemberDeclarationWithArrayType() {
    172         String code = "public @interface ClassPreamble { String[] author(); }";
    173         considerCode(code);
    174 
    175         AnnotationDeclaration ad = cu.getAnnotationDeclarationByName("ClassPreamble").get();
    176         AnnotationMemberDeclaration md = (AnnotationMemberDeclaration)ad.getMember(0);
    177         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(md);
    178         assertEquals(Arrays.asList("String[]", " ", "author", "(", ")", ";"),
    179                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    180     }
    181 
    182     @Test
    183     public void checkNodeTextCreatedAnnotationMemberDeclarationArrayType() {
    184         String code = "public @interface ClassPreamble { String[] author(); }";
    185         considerCode(code);
    186 
    187         AnnotationDeclaration ad = cu.getAnnotationDeclarationByName("ClassPreamble").get();
    188         AnnotationMemberDeclaration md = (AnnotationMemberDeclaration)ad.getMember(0).asAnnotationMemberDeclaration();
    189         Type type = md.getType();
    190         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(type);
    191         assertEquals(Arrays.asList("String", "[", "]"),
    192                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    193     }
    194 
    195     @Test
    196     public void checkNodeTextCreatedAnnotationMemberDeclarationWithComment() throws IOException {
    197         considerExample("AnnotationDeclaration_Example3_original");
    198 
    199         AnnotationMemberDeclaration md = (AnnotationMemberDeclaration)cu.getAnnotationDeclarationByName("ClassPreamble").get().getMember(5).asAnnotationMemberDeclaration();
    200         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(md);
    201         assertEquals(Arrays.asList("String[]", " ", "reviewers", "(", ")", ";"),
    202                 nodeText.getElements().stream().map(TextElement::expand).collect(Collectors.toList()));
    203     }
    204 
    205     @Test
    206     public void checkNodeTextCreatedArrayCreationLevelWithoutExpression() throws IOException {
    207         considerExpression("new int[]");
    208 
    209         ArrayCreationExpr arrayCreationExpr = (ArrayCreationExpr)expression.asArrayCreationExpr();
    210         ArrayCreationLevel arrayCreationLevel = arrayCreationExpr.getLevels().get(0);
    211         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(arrayCreationLevel);
    212         assertEquals(Arrays.asList("[", "]"),
    213                 nodeText.getElements().stream().map(TextElement::expand).filter(e -> !e.isEmpty()).collect(Collectors.toList()));
    214     }
    215 
    216     @Test
    217     public void checkNodeTextCreatedArrayCreationLevelWith() throws IOException {
    218         considerExpression("new int[123]");
    219 
    220         ArrayCreationExpr arrayCreationExpr = (ArrayCreationExpr)expression.asArrayCreationExpr();
    221         ArrayCreationLevel arrayCreationLevel = arrayCreationExpr.getLevels().get(0);
    222         NodeText nodeText = LexicalPreservingPrinter.getOrCreateNodeText(arrayCreationLevel);
    223         assertEquals(Arrays.asList("[", "123", "]"),
    224                 nodeText.getElements().stream().map(TextElement::expand).filter(e -> !e.isEmpty()).collect(Collectors.toList()));
    225     }
    226 
    227     //
    228     // Tests on findIndentation
    229     //
    230 
    231     @Test
    232     public void findIndentationForAnnotationMemberDeclarationWithoutComment() throws IOException {
    233         considerExample("AnnotationDeclaration_Example3_original");
    234         Node node = cu.getAnnotationDeclarationByName("ClassPreamble").get().getMember(4);
    235         List<TokenTextElement> indentation = LexicalPreservingPrinter.findIndentation(node);
    236         assertEquals(Arrays.asList(" ", " ", " "), indentation.stream().map(TokenTextElement::expand).collect(Collectors.toList()));
    237     }
    238 
    239     @Test
    240     public void findIndentationForAnnotationMemberDeclarationWithComment() throws IOException {
    241         considerExample("AnnotationDeclaration_Example3_original");
    242         Node node = cu.getAnnotationDeclarationByName("ClassPreamble").get().getMember(5);
    243         List<TokenTextElement> indentation = LexicalPreservingPrinter.findIndentation(node);
    244         assertEquals(Arrays.asList(" ", " ", " "), indentation.stream().map(TokenTextElement::expand).collect(Collectors.toList()));
    245     }
    246 
    247     //
    248     // Tests on printing
    249     //
    250 
    251     @Test
    252     public void printASuperSimpleCUWithoutChanges() {
    253         String code = "class A {}";
    254         considerCode(code);
    255 
    256         assertEquals(code, LexicalPreservingPrinter.print(cu));
    257     }
    258 
    259     @Test
    260     public void printASuperSimpleClassWithAFieldAdded() {
    261         String code = "class A {}";
    262         considerCode(code);
    263 
    264         ClassOrInterfaceDeclaration classA = cu.getClassByName("A").get();
    265         classA.addField("int", "myField");
    266         assertEquals("class A {" + EOL + "    int myField;"+EOL+"}", LexicalPreservingPrinter.print(classA));
    267     }
    268 
    269     @Test
    270     public void printASuperSimpleClassWithoutChanges() {
    271         String code = "class A {}";
    272         considerCode(code);
    273 
    274         assertEquals(code, LexicalPreservingPrinter.print(cu.getClassByName("A").get()));
    275     }
    276 
    277     @Test
    278     public void printASimpleCUWithoutChanges() {
    279         String code = "class /*a comment*/ A {\t\t"+EOL+" int f;"+EOL+EOL+EOL+"         void foo(int p  ) { return  'z'  \t; }}";
    280         considerCode(code);
    281 
    282         assertEquals(code, LexicalPreservingPrinter.print(cu));
    283         assertEquals(code, LexicalPreservingPrinter.print(cu.getClassByName("A").get()));
    284         assertEquals("void foo(int p  ) { return  'z'  \t; }", LexicalPreservingPrinter.print(cu.getClassByName("A").get().getMethodsByName("foo").get(0)));
    285     }
    286 
    287     @Test
    288     public void printASimpleClassRemovingAField() {
    289         String code = "class /*a comment*/ A {\t\t"+EOL+" int f;"+EOL+EOL+EOL+"         void foo(int p  ) { return  'z'  \t; }}";
    290         considerCode(code);
    291 
    292         ClassOrInterfaceDeclaration c = cu.getClassByName("A").get();
    293         c.getMembers().remove(0);
    294         assertEquals("class /*a comment*/ A {\t\t"+ EOL +
    295                 EOL +
    296                 "         void foo(int p  ) { return  'z'  \t; }}", LexicalPreservingPrinter.print(c));
    297     }
    298 
    299     @Test
    300     public void printASimpleMethodAddingAParameterToAMethodWithZeroParameters() {
    301         String code = "class A { void foo() {} }";
    302         considerCode(code);
    303 
    304         MethodDeclaration m = cu.getClassByName("A").get().getMethodsByName("foo").get(0);
    305         m.addParameter("float", "p1");
    306         assertEquals("void foo(float p1) {}", LexicalPreservingPrinter.print(m));
    307     }
    308 
    309     @Test
    310     public void printASimpleMethodAddingAParameterToAMethodWithOneParameter() {
    311         String code = "class A { void foo(char p1) {} }";
    312         considerCode(code);
    313 
    314         MethodDeclaration m = cu.getClassByName("A").get().getMethodsByName("foo").get(0);
    315         m.addParameter("float", "p2");
    316         assertEquals("void foo(char p1, float p2) {}", LexicalPreservingPrinter.print(m));
    317     }
    318 
    319     @Test
    320     public void printASimpleMethodRemovingAParameterToAMethodWithOneParameter() {
    321         String code = "class A { void foo(float p1) {} }";
    322         considerCode(code);
    323 
    324         MethodDeclaration m = cu.getClassByName("A").get().getMethodsByName("foo").get(0);
    325         m.getParameters().remove(0);
    326         assertEquals("void foo() {}", LexicalPreservingPrinter.print(m));
    327     }
    328 
    329     @Test
    330     public void printASimpleMethodRemovingParameterOneFromMethodWithTwoParameters() {
    331         String code = "class A { void foo(char p1, int p2) {} }";
    332         considerCode(code);
    333 
    334         MethodDeclaration m = cu.getClassByName("A").get().getMethodsByName("foo").get(0);
    335         m.getParameters().remove(0);
    336         assertEquals("void foo(int p2) {}", LexicalPreservingPrinter.print(m));
    337     }
    338 
    339     @Test
    340     public void printASimpleMethodRemovingParameterTwoFromMethodWithTwoParameters() {
    341         String code = "class A { void foo(char p1, int p2) {} }";
    342         considerCode(code);
    343 
    344         MethodDeclaration m = cu.getClassByName("A").get().getMethodsByName("foo").get(0);
    345         m.getParameters().remove(1);
    346         assertEquals("void foo(char p1) {}", LexicalPreservingPrinter.print(m));
    347     }
    348 
    349     @Test
    350     public void printASimpleMethodAddingAStatement() {
    351         String code = "class A { void foo(char p1, int p2) {} }";
    352         considerCode(code);
    353 
    354         Statement s = new ExpressionStmt(new BinaryExpr(
    355                 new IntegerLiteralExpr("10"), new IntegerLiteralExpr("2"), BinaryExpr.Operator.PLUS
    356         ));
    357         NodeList<Statement> stmts = cu.getClassByName("A").get().getMethodsByName("foo").get(0).getBody().get().getStatements();
    358         stmts.add(s);
    359         MethodDeclaration m = cu.getClassByName("A").get().getMethodsByName("foo").get(0);
    360         assertEquals("void foo(char p1, int p2) {"+EOL +
    361                 "    10 + 2;"+ EOL +
    362                 "}", LexicalPreservingPrinter.print(m));
    363     }
    364 
    365     @Test
    366     public void printASimpleImport() {
    367         String code = "import a.b.c.D;";
    368         considerCode(code);
    369 
    370         ImportDeclaration imp = (ImportDeclaration)cu.getChildNodes().get(0);
    371         assertEquals("import a.b.c.D;", LexicalPreservingPrinter.print(imp));
    372     }
    373 
    374     @Test
    375     public void printAnotherImport() {
    376         String code = "import com.github.javaparser.ast.CompilationUnit;";
    377         considerCode(code);
    378 
    379         ImportDeclaration imp = (ImportDeclaration)cu.getChildNodes().get(0);
    380         assertEquals("import com.github.javaparser.ast.CompilationUnit;", LexicalPreservingPrinter.print(imp));
    381     }
    382 
    383     @Test
    384     public void printAStaticImport() {
    385         String code = "import static com.github.javaparser.ParseStart.*;";
    386         considerCode(code);
    387 
    388         ImportDeclaration imp = (ImportDeclaration)cu.getChildNodes().get(0);
    389         assertEquals("import static com.github.javaparser.ParseStart.*;", LexicalPreservingPrinter.print(imp));
    390     }
    391 
    392     @Test
    393     public void checkAnnidatedTypeParametersPrinting() {
    394         String code = "class A { private final Stack<Iterator<Triple>> its = new Stack<Iterator<Triple>>(); }";
    395         considerCode(code);
    396         assertEquals("class A { private final Stack<Iterator<Triple>> its = new Stack<Iterator<Triple>>(); }", LexicalPreservingPrinter.print(cu));
    397     }
    398 
    399     @Test
    400     public void printASingleCatch() {
    401         String code = "class A {{try { doit(); } catch (Exception e) {}}}";
    402         considerCode(code);
    403 
    404         assertEquals("class A {{try { doit(); } catch (Exception e) {}}}", LexicalPreservingPrinter.print(cu));
    405     }
    406 
    407     @Test
    408     public void printAMultiCatch() {
    409         String code = "class A {{try { doit(); } catch (Exception | AssertionError e) {}}}";
    410         considerCode(code);
    411 
    412         assertEquals("class A {{try { doit(); } catch (Exception | AssertionError e) {}}}", LexicalPreservingPrinter.print(cu));
    413     }
    414 
    415     @Test
    416     public void printASingleCatchType() {
    417         String code = "class A {{try { doit(); } catch (Exception e) {}}}";
    418         considerCode(code);
    419         InitializerDeclaration initializerDeclaration = (InitializerDeclaration)cu.getType(0).getMembers().get(0);
    420         TryStmt tryStmt = (TryStmt)initializerDeclaration.getBody().getStatements().get(0);
    421         CatchClause catchClause = tryStmt.getCatchClauses().get(0);
    422         Type catchType = catchClause.getParameter().getType();
    423 
    424         assertEquals("Exception", LexicalPreservingPrinter.print(catchType));
    425     }
    426 
    427     @Test
    428     public void printUnionType() {
    429         String code = "class A {{try { doit(); } catch (Exception | AssertionError e) {}}}";
    430         considerCode(code);
    431         InitializerDeclaration initializerDeclaration = (InitializerDeclaration)cu.getType(0).getMembers().get(0);
    432         TryStmt tryStmt = (TryStmt)initializerDeclaration.getBody().getStatements().get(0);
    433         CatchClause catchClause = tryStmt.getCatchClauses().get(0);
    434         UnionType unionType = (UnionType)catchClause.getParameter().getType();
    435 
    436         assertEquals("Exception | AssertionError", LexicalPreservingPrinter.print(unionType));
    437     }
    438 
    439     @Test
    440     public void printParameterHavingUnionType() {
    441         String code = "class A {{try { doit(); } catch (Exception | AssertionError e) {}}}";
    442         considerCode(code);
    443         InitializerDeclaration initializerDeclaration = (InitializerDeclaration)cu.getType(0).getMembers().get(0);
    444         TryStmt tryStmt = (TryStmt)initializerDeclaration.getBody().getStatements().get(0);
    445         CatchClause catchClause = tryStmt.getCatchClauses().get(0);
    446         Parameter parameter = catchClause.getParameter();
    447 
    448         assertEquals("Exception | AssertionError e", LexicalPreservingPrinter.print(parameter));
    449     }
    450 
    451     @Test
    452     public void printLambaWithUntypedParams() {
    453         String code = "class A {Function<String,String> f = a -> a;}";
    454         considerCode(code);
    455 
    456         assertEquals("class A {Function<String,String> f = a -> a;}", LexicalPreservingPrinter.print(cu));
    457     }
    458 
    459     @Test
    460     public void printAModuleInfoSpecificKeywordUsedAsIdentifier1() {
    461         considerCode("class module { }");
    462 
    463         cu.getClassByName("module").get().setName("xyz");
    464 
    465         assertEquals("class xyz { }", LexicalPreservingPrinter.print(cu));
    466     }
    467 
    468     @Test
    469     public void printAModuleInfoSpecificKeywordUsedAsIdentifier2() {
    470         considerCode("class xyz { }");
    471 
    472         cu.getClassByName("xyz").get().setName("module");
    473 
    474         assertEquals("class module { }", LexicalPreservingPrinter.print(cu));
    475     }
    476 
    477     // Issue 823: setPackageDeclaration on CU starting with a comment
    478     @Test
    479     public void reactToSetPackageDeclarationOnCuStartingWithComment() {
    480         considerCode("// Hey, this is a comment\n" +
    481                 "\n" +
    482                 "\n" +
    483                 "// Another one\n" +
    484                 "\n" +
    485                 "class A {}");
    486         cu.setPackageDeclaration("org.javaparser.lexicalpreservation.examples");
    487     }
    488 
    489     @Test
    490     public void printLambdaIntersectionTypeAssignment() {
    491         String code = "class A {" + EOL +
    492                 "  void f() {" + EOL +
    493                 "    Runnable r = (Runnable & Serializable) (() -> {});" + EOL +
    494                 "    r = (Runnable & Serializable)() -> {};" + EOL +
    495                 "    r = (Runnable & I)() -> {};" + EOL +
    496                 "  }}";
    497         considerCode(code);
    498 
    499         assertEquals(code, LexicalPreservingPrinter.print(cu));
    500     }
    501 
    502     @Test
    503     public void printLambdaIntersectionTypeReturn() {
    504         String code = "class A {" + EOL
    505                 + "  Object f() {" + EOL
    506                 + "    return (Comparator<Map.Entry<K, V>> & Serializable)(c1, c2) -> c1.getKey().compareTo(c2.getKey()); " + EOL
    507                 + "}}";
    508         considerCode(code);
    509 
    510         assertEquals(code, LexicalPreservingPrinter.print(cu));
    511     }
    512 
    513     // See issue #855
    514     @Test
    515     public void handleOverrideAnnotation() {
    516         String code = "public class TestPage extends Page {" + EOL +
    517                 EOL +
    518                 "   protected void test() {}" + EOL +
    519                 EOL +
    520                 "   @Override" + EOL +
    521                 "   protected void initializePage() {}" + EOL +
    522                 "}";
    523 
    524         CompilationUnit cu = JavaParser.parse(code);
    525         LexicalPreservingPrinter.setup(cu);
    526 
    527         cu.getTypes()
    528                 .forEach(type -> type.getMembers()
    529                         .forEach(member -> {
    530                             if (member instanceof MethodDeclaration) {
    531                                 MethodDeclaration methodDeclaration = (MethodDeclaration) member;
    532                                 if (!methodDeclaration.getAnnotationByName("Override").isPresent()) {
    533                                     methodDeclaration.addAnnotation("Override");
    534                                 }
    535                             }
    536                         }));
    537         assertEquals("public class TestPage extends Page {" + EOL +
    538                 EOL +
    539                 "   @Override()" + EOL +
    540                 "   protected void test() {}" + EOL +
    541                 EOL +
    542                 "   @Override" + EOL +
    543                 "   protected void initializePage() {}" + EOL +
    544                 "}", LexicalPreservingPrinter.print(cu));
    545     }
    546 
    547     @Test
    548     public void preserveSpaceAsIsForASimpleClassWithMoreFormatting() throws IOException {
    549         considerExample("ASimpleClassWithMoreFormatting");
    550         assertEquals(readExample("ASimpleClassWithMoreFormatting"), LexicalPreservingPrinter.print(cu));
    551     }
    552 
    553     @Test
    554     public void renameASimpleClassWithMoreFormatting() throws IOException {
    555         considerExample("ASimpleClassWithMoreFormatting");
    556 
    557         cu.getClassByName("ASimpleClass").get()
    558                 .setName("MyRenamedClass");
    559         assertEquals(readExample("ASimpleClassWithMoreFormatting_step1"), LexicalPreservingPrinter.print(cu));
    560     }
    561 
    562     @Test
    563     public void theLexicalPreservationStringForAnAddedMethodShouldBeIndented() throws IOException {
    564         considerExample("ASimpleClassWithMoreFormatting");
    565 
    566         cu.getClassByName("ASimpleClass").get()
    567                 .setName("MyRenamedClass");
    568         MethodDeclaration setter = cu
    569                 .getClassByName("MyRenamedClass").get()
    570                 .addMethod("setAField", Modifier.PUBLIC);
    571         assertEquals("public void setAField() {" + EOL +
    572                 "    }", LexicalPreservingPrinter.print(setter));
    573     }
    574 
    575     @Test
    576     public void addMethodToASimpleClassWithMoreFormatting() throws IOException {
    577         considerExample("ASimpleClassWithMoreFormatting");
    578 
    579         cu.getClassByName("ASimpleClass").get()
    580                 .setName("MyRenamedClass");
    581         MethodDeclaration setter = cu
    582                 .getClassByName("MyRenamedClass").get()
    583                 .addMethod("setAField", Modifier.PUBLIC);
    584         assertEquals(readExample("ASimpleClassWithMoreFormatting_step2"), LexicalPreservingPrinter.print(cu));
    585     }
    586 
    587     @Test
    588     public void addingParameterToAnAddedMethodInASimpleClassWithMoreFormatting() throws IOException {
    589         considerExample("ASimpleClassWithMoreFormatting");
    590 
    591         cu.getClassByName("ASimpleClass").get()
    592                 .setName("MyRenamedClass");
    593         MethodDeclaration setter = cu
    594                 .getClassByName("MyRenamedClass").get()
    595                 .addMethod("setAField", Modifier.PUBLIC);
    596         setter.addParameter("boolean", "aField");
    597         assertEquals(readExample("ASimpleClassWithMoreFormatting_step3"), LexicalPreservingPrinter.print(cu));
    598     }
    599 
    600     @Test
    601     public void findIndentationOfEmptyMethod() throws IOException {
    602         considerExample("ASimpleClassWithMoreFormatting_step3");
    603 
    604         MethodDeclaration setter = cu.getClassByName("MyRenamedClass").get()
    605                 .getMethodsByName("setAField").get(0);
    606         assertEquals(4, LexicalPreservingPrinter.findIndentation(setter).size());
    607         assertEquals(4, LexicalPreservingPrinter.findIndentation(setter.getBody().get()).size());
    608     }
    609 
    610     @Test
    611     public void findIndentationOfMethodWithStatements() throws IOException {
    612         considerExample("ASimpleClassWithMoreFormatting_step4");
    613 
    614         MethodDeclaration setter = cu.getClassByName("MyRenamedClass").get()
    615                 .getMethodsByName("setAField").get(0);
    616         assertEquals(4, LexicalPreservingPrinter.findIndentation(setter).size());
    617         assertEquals(4, LexicalPreservingPrinter.findIndentation(setter.getBody().get()).size());
    618         assertEquals(8, LexicalPreservingPrinter.findIndentation(setter.getBody().get().getStatement(0)).size());
    619     }
    620 
    621     @Test
    622     public void addingStatementToAnAddedMethodInASimpleClassWithMoreFormatting() throws IOException {
    623         considerExample("ASimpleClassWithMoreFormatting");
    624 
    625         cu.getClassByName("ASimpleClass").get()
    626                 .setName("MyRenamedClass");
    627         MethodDeclaration setter = cu
    628                 .getClassByName("MyRenamedClass").get()
    629                 .addMethod("setAField", Modifier.PUBLIC);
    630         setter.addParameter("boolean", "aField");
    631         setter.getBody().get().getStatements().add(new ExpressionStmt(
    632                 new AssignExpr(
    633                         new FieldAccessExpr(new ThisExpr(),"aField"),
    634                         new NameExpr("aField"),
    635                         AssignExpr.Operator.ASSIGN
    636                 )));
    637         assertEquals(readExample("ASimpleClassWithMoreFormatting_step4"), LexicalPreservingPrinter.print(cu));
    638     }
    639 
    640     @Test
    641     public void addingStatementToAnAddedMethodInASimpleClassWithMoreFormattingFromStep3() throws IOException {
    642         considerExample("ASimpleClassWithMoreFormatting_step3");
    643 
    644         MethodDeclaration setter = cu.getClassByName("MyRenamedClass").get()
    645                 .getMethodsByName("setAField").get(0);
    646         setter.getBody().get().getStatements().add(new ExpressionStmt(
    647                 new AssignExpr(
    648                         new FieldAccessExpr(new ThisExpr(),"aField"),
    649                         new NameExpr("aField"),
    650                         AssignExpr.Operator.ASSIGN
    651                 )));
    652         assertEquals(readExample("ASimpleClassWithMoreFormatting_step4"), LexicalPreservingPrinter.print(cu));
    653     }
    654 
    655     @Test
    656     public void nodeTextForMethod() throws IOException {
    657         considerExample("ASimpleClassWithMoreFormatting_step4");
    658 
    659         MethodDeclaration setter = cu.getClassByName("MyRenamedClass").get()
    660                 .getMethodsByName("setAField").get(0);
    661         NodeText nodeText;
    662 
    663         nodeText = getTextForNode(setter);
    664         int index = 0;
    665         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.PUBLIC));
    666         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    667         assertTrue(nodeText.getElements().get(index++).isChildOfClass(VoidType.class));
    668         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    669         assertTrue(nodeText.getElements().get(index++).isChildOfClass(SimpleName.class));
    670         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.LPAREN));
    671         assertTrue(nodeText.getElements().get(index++).isChildOfClass(Parameter.class));
    672         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.RPAREN));
    673         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    674         assertTrue(nodeText.getElements().get(index++).isChildOfClass(BlockStmt.class));
    675         assertEquals(index, nodeText.getElements().size());
    676 
    677         nodeText = getTextForNode(setter.getBody().get());
    678         index = 0;
    679         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.LBRACE));
    680         assertTrue(nodeText.getElements().get(index++).isNewline());
    681         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    682         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    683         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    684         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    685         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    686         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    687         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    688         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    689         assertTrue(nodeText.getElements().get(index++).isChildOfClass(ExpressionStmt.class));
    690         assertTrue(nodeText.getElements().get(index++).isNewline());
    691         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    692         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    693         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    694         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    695         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.RBRACE));
    696         assertEquals(index, nodeText.getElements().size());
    697 
    698         nodeText = getTextForNode(setter.getBody().get().getStatement(0));
    699         index = 0;
    700         assertTrue(nodeText.getElements().get(index++).isChildOfClass(AssignExpr.class));
    701         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SEMICOLON));
    702         assertEquals(index, nodeText.getElements().size());
    703     }
    704 
    705     @Test
    706     public void nodeTextForModifiedMethod() throws IOException {
    707         considerExample("ASimpleClassWithMoreFormatting_step3");
    708 
    709         MethodDeclaration setter = cu.getClassByName("MyRenamedClass").get()
    710                 .getMethodsByName("setAField").get(0);
    711         setter.getBody().get().getStatements().add(new ExpressionStmt(
    712                 new AssignExpr(
    713                         new FieldAccessExpr(new ThisExpr(),"aField"),
    714                         new NameExpr("aField"),
    715                         AssignExpr.Operator.ASSIGN
    716                 )));
    717         NodeText nodeText;
    718 
    719         nodeText = getTextForNode(setter);
    720         int index = 0;
    721         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.PUBLIC));
    722         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    723         assertTrue(nodeText.getElements().get(index++).isChildOfClass(VoidType.class));
    724         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    725         assertTrue(nodeText.getElements().get(index++).isChildOfClass(SimpleName.class));
    726         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.LPAREN));
    727         assertTrue(nodeText.getElements().get(index++).isChildOfClass(Parameter.class));
    728         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.RPAREN));
    729         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    730         assertTrue(nodeText.getElements().get(index++).isChildOfClass(BlockStmt.class));
    731         assertEquals(index, nodeText.getElements().size());
    732 
    733         nodeText = getTextForNode(setter.getBody().get());
    734         index = 0;
    735         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.LBRACE));
    736         assertTrue(nodeText.getElements().get(index++).isNewline());
    737         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    738         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    739         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    740         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    741         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    742         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    743         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    744         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    745         assertTrue(nodeText.getElements().get(index++).isChildOfClass(ExpressionStmt.class));
    746         assertTrue(nodeText.getElements().get(index++).isNewline());
    747         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    748         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    749         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    750         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SPACE));
    751         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.RBRACE));
    752         assertEquals(index, nodeText.getElements().size());
    753 
    754         nodeText = LexicalPreservingPrinter.getOrCreateNodeText(setter.getBody().get().getStatement(0));
    755         index = 0;
    756         assertTrue(nodeText.getElements().get(index++).isChildOfClass(AssignExpr.class));
    757         assertTrue(nodeText.getElements().get(index++).isToken(GeneratedJavaParserConstants.SEMICOLON));
    758         assertEquals(index, nodeText.getElements().size());
    759     }
    760 
    761     // See issue #926
    762     @Test
    763     public void addASecondStatementToExistingMethod() throws IOException {
    764         considerExample("MethodWithOneStatement");
    765 
    766         MethodDeclaration methodDeclaration = cu.getType(0).getMethodsByName("someMethod").get(0);
    767         methodDeclaration.getBody().get().getStatements().add(new ExpressionStmt(
    768                 new VariableDeclarationExpr(
    769                         new VariableDeclarator(
    770                                 JavaParser.parseClassOrInterfaceType("String"),
    771                                 "test2",
    772                                 new StringLiteralExpr("")))
    773         ));
    774         assertEquals("public void someMethod() {" + EOL
    775                 + "        String test = \"\";" + EOL
    776                 + "        String test2 = \"\";" + EOL
    777                 + "    }", LexicalPreservingPrinter.print(methodDeclaration));
    778     }
    779 
    780     // See issue #866
    781     @Test
    782     public void moveOverrideAnnotations() {
    783         String code = "public class TestPage extends Page {" + EOL +
    784                 EOL +
    785                 "   protected void test() {}" + EOL +
    786                 EOL +
    787                 "   protected @Override void initializePage() {}" + EOL +
    788                 "}";
    789 
    790         CompilationUnit cu = JavaParser.parse(code);
    791         LexicalPreservingPrinter.setup(cu);
    792 
    793         cu.getTypes()
    794                 .forEach(type -> {
    795                     type.getMembers()
    796                             .forEach(member -> {
    797                                 member.ifMethodDeclaration(methodDeclaration -> {
    798                                     if (methodDeclaration.getAnnotationByName("Override").isPresent()) {
    799 
    800                                         while (methodDeclaration.getAnnotations().isNonEmpty()) {
    801                                             AnnotationExpr annotationExpr = methodDeclaration.getAnnotations().get(0);
    802                                             annotationExpr.remove();
    803                                         }
    804 
    805                                         methodDeclaration.addMarkerAnnotation("Override");
    806                                     }
    807                                 });
    808                             });
    809                 });
    810         assertEquals("public class TestPage extends Page {" + EOL +
    811                 EOL +
    812                 "   protected void test() {}" + EOL +
    813                 EOL +
    814                 "   @Override" + EOL +
    815                 "   protected void initializePage() {}" + EOL +
    816                 "}", LexicalPreservingPrinter.print(cu));
    817     }
    818 
    819     // See issue #866
    820     @Test
    821     public void moveOrAddOverrideAnnotations() {
    822         String code = "public class TestPage extends Page {" + EOL +
    823                 EOL +
    824                 "   protected void test() {}" + EOL +
    825                 EOL +
    826                 "   protected @Override void initializePage() {}" + EOL +
    827                 "}";
    828 
    829         CompilationUnit cu = JavaParser.parse(code);
    830         LexicalPreservingPrinter.setup(cu);
    831 
    832         cu.getTypes()
    833                 .forEach(type -> {
    834                     type.getMembers()
    835                             .forEach(member -> {
    836                                 if (member instanceof MethodDeclaration) {
    837                                     MethodDeclaration methodDeclaration = (MethodDeclaration) member;
    838                                     if (methodDeclaration.getAnnotationByName("Override").isPresent()) {
    839 
    840                                         while (methodDeclaration.getAnnotations().isNonEmpty()) {
    841                                             AnnotationExpr annotationExpr = methodDeclaration.getAnnotations().get(0);
    842                                             annotationExpr.remove();
    843                                         }
    844                                     }
    845                                     methodDeclaration.addMarkerAnnotation("Override");
    846                                 }
    847                             });
    848                 });
    849         assertEquals("public class TestPage extends Page {" + EOL +
    850                 EOL +
    851                 "   @Override" + EOL +
    852                 "   protected void test() {}" + EOL +
    853                 EOL +
    854                 "   @Override" + EOL +
    855                 "   protected void initializePage() {}" + EOL +
    856                 "}", LexicalPreservingPrinter.print(cu));
    857     }
    858 
    859     // See issue #865
    860     @Test
    861     public void handleAddingMarkerAnnotation() {
    862         String code = "public class TestPage extends Page {" + EOL +
    863                 EOL +
    864                 "   protected void test() {}" + EOL +
    865                 EOL +
    866                 "   @Override" + EOL +
    867                 "   protected void initializePage() {}" + EOL +
    868                 "}";
    869 
    870         CompilationUnit cu = JavaParser.parse(code);
    871         LexicalPreservingPrinter.setup(cu);
    872 
    873         cu.getTypes()
    874                 .forEach(type -> {
    875                     type.getMembers()
    876                             .forEach(member -> {
    877                                 if (member instanceof MethodDeclaration) {
    878                                     MethodDeclaration methodDeclaration = (MethodDeclaration) member;
    879                                     if (!methodDeclaration.getAnnotationByName("Override").isPresent()) {
    880                                         methodDeclaration.addMarkerAnnotation("Override");
    881                                     }
    882                                 }
    883                             });
    884                 });
    885         assertEquals("public class TestPage extends Page {" + EOL +
    886                 EOL +
    887                 "   @Override" + EOL +
    888                 "   protected void test() {}" + EOL +
    889                 EOL +
    890                 "   @Override" + EOL +
    891                 "   protected void initializePage() {}" + EOL +
    892                 "}", LexicalPreservingPrinter.print(cu));
    893     }
    894 
    895     // See issue #865
    896     @Test
    897     public void handleOverrideMarkerAnnotation() {
    898         String code = "public class TestPage extends Page {" + EOL +
    899                 EOL +
    900                 "   protected void test() {}" + EOL +
    901                 EOL +
    902                 "   protected void initializePage() {}" + EOL +
    903                 "}";
    904 
    905         CompilationUnit cu = JavaParser.parse(code);
    906         LexicalPreservingPrinter.setup(cu);
    907 
    908         cu.getTypes()
    909                 .forEach(type -> type.getMembers()
    910                         .forEach(member ->
    911                             member.ifMethodDeclaration(methodDeclaration -> methodDeclaration.addMarkerAnnotation("Override")
    912                         )));
    913         assertEquals("public class TestPage extends Page {" + EOL +
    914                 EOL +
    915                 "   @Override" + EOL +
    916                 "   protected void test() {}" + EOL +
    917                 EOL +
    918                 "   @Override" + EOL +
    919                 "   protected void initializePage() {}" + EOL +
    920                 "}", LexicalPreservingPrinter.print(cu));
    921     }
    922 
    923     // See issue #865
    924     @Test
    925     public void handleOverrideAnnotationAlternative() {
    926         String code = "public class TestPage extends Page {" + EOL +
    927                 EOL +
    928                 "   protected void test() {}" + EOL +
    929                 EOL +
    930                 "   protected void initializePage() {}" + EOL +
    931                 "}";
    932 
    933         CompilationUnit cu = JavaParser.parse(code);
    934         LexicalPreservingPrinter.setup(cu);
    935 
    936         cu.getTypes()
    937                 .forEach(type -> type.getMembers()
    938                         .forEach(member ->  member.ifMethodDeclaration(methodDeclaration -> methodDeclaration.addAnnotation("Override"))));
    939         assertEquals("public class TestPage extends Page {" + EOL +
    940                 EOL +
    941                 "   @Override()" + EOL +
    942                 "   protected void test() {}" + EOL +
    943                 EOL +
    944                 "   @Override()" + EOL +
    945                 "   protected void initializePage() {}" + EOL +
    946                 "}", LexicalPreservingPrinter.print(cu));
    947     }
    948 
    949     @Test
    950     public void invokeModifierVisitor() {
    951         String code = "class A {" + EOL
    952                 + "  Object f() {" + EOL
    953                 + "    return (Comparator<Map.Entry<K, V>> & Serializable)(c1, c2) -> c1.getKey().compareTo(c2.getKey()); " + EOL
    954                 + "}}";
    955         CompilationUnit cu = JavaParser.parse(code);
    956         LexicalPreservingPrinter.setup(cu);
    957         cu.accept(new ModifierVisitor<>(), null);
    958     }
    959 
    960     @Test
    961     public void handleDeprecatedAnnotationFinalClass() {
    962         String code = "public final class A {}";
    963 
    964         CompilationUnit cu = JavaParser.parse(code);
    965         LexicalPreservingPrinter.setup(cu);
    966 
    967         cu.getTypes().forEach(type -> type.addAndGetAnnotation(Deprecated.class));
    968 
    969         assertEquals("@Deprecated()" + EOL +
    970                 "public final class A {}" , LexicalPreservingPrinter.print(cu));
    971 
    972     }
    973 
    974     @Test
    975     public void handleDeprecatedAnnotationAbstractClass() {
    976         String code = "public abstract class A {}";
    977 
    978         CompilationUnit cu = JavaParser.parse(code);
    979         LexicalPreservingPrinter.setup(cu);
    980 
    981         cu.getTypes().forEach(type -> type.addAndGetAnnotation(Deprecated.class));
    982 
    983         assertEquals("@Deprecated()" + EOL +
    984                 "public abstract class A {}" , LexicalPreservingPrinter.print(cu));
    985     }
    986 
    987     @Test
    988     public void issue1244() {
    989         String code = "public class Foo {" + EOL + EOL
    990                 + "// Some comment" + EOL + EOL // does work with only one \n
    991                 + "public void writeExternal() {}" + EOL + "}";
    992         CompilationUnit originalCu = JavaParser.parse(code);
    993         CompilationUnit cu = LexicalPreservingPrinter.setup(originalCu);
    994 
    995         cu.findAll(ClassOrInterfaceDeclaration.class).stream().forEach(c -> {
    996             List<MethodDeclaration> methods = c.getMethodsByName("writeExternal");
    997             for (MethodDeclaration method : methods) {
    998                 c.remove(method);
    999             }
   1000         });
   1001         assertEqualsNoEol("public class Foo {\n" +
   1002                 "// Some comment\n\n" +
   1003                 "}", LexicalPreservingPrinter.print(cu));
   1004     }
   1005 
   1006     static class AddFooCallModifierVisitor extends ModifierVisitor<Void> {
   1007         @Override
   1008         public Visitable visit(MethodCallExpr n, Void arg) {
   1009             // Add a call to foo() on every found method call
   1010             return new MethodCallExpr(n, "foo");
   1011         }
   1012     }
   1013 
   1014     // See issue 1277
   1015     @Test
   1016     public void testInvokeModifierVisitor() throws IOException {
   1017         String code = "class A {" + EOL +
   1018                 "  public String message = \"hello\";" + EOL +
   1019                 "   void bar() {" + EOL +
   1020                 "     System.out.println(\"hello\");" + EOL +
   1021                 "   }" + EOL +
   1022                 "}";
   1023 
   1024         CompilationUnit cu = JavaParser.parse(code);
   1025         LexicalPreservingPrinter.setup(cu);
   1026         cu.accept(new AddFooCallModifierVisitor(), null);
   1027     }
   1028 
   1029     static class CallModifierVisitor extends ModifierVisitor<Void> {
   1030         @Override
   1031         public Visitable visit(MethodCallExpr n, Void arg) {
   1032             // Add a call to foo() on every found method call
   1033             return new MethodCallExpr(n.clone(), "foo");
   1034         }
   1035     }
   1036 
   1037     @Test
   1038     public void invokeModifierVisitorIssue1297() {
   1039         String code = "class A {" + EOL +
   1040                 "   public void bar() {" + EOL +
   1041                 "     System.out.println(\"hello\");" + EOL +
   1042                 "     System.out.println(\"hello\");" + EOL +
   1043                 "     // comment" + EOL +
   1044                 "   }" + EOL +
   1045                 "}";
   1046 
   1047         CompilationUnit cu = JavaParser.parse(code);
   1048         LexicalPreservingPrinter.setup(cu);
   1049         cu.accept(new CallModifierVisitor(), null);
   1050     }
   1051 }
   1052