• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
3  * Copyright (C) 2011, 2013-2016 The JavaParser Team.
4  *
5  * This file is part of JavaParser.
6  *
7  * JavaParser can be used either under the terms of
8  * a) the GNU Lesser General Public License as published by
9  *     the Free Software Foundation, either version 3 of the License, or
10  *     (at your option) any later version.
11  * b) the terms of the Apache License
12  *
13  * You should have received a copy of both licenses in LICENCE.LGPL and
14  * LICENCE.APACHE. Please refer to those files for details.
15  *
16  * JavaParser is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Lesser General Public License for more details.
20  */
21 
22 package com.github.javaparser.printer;
23 
24 import com.github.javaparser.ast.*;
25 import com.github.javaparser.ast.body.*;
26 import com.github.javaparser.ast.comments.BlockComment;
27 import com.github.javaparser.ast.comments.Comment;
28 import com.github.javaparser.ast.comments.JavadocComment;
29 import com.github.javaparser.ast.comments.LineComment;
30 import com.github.javaparser.ast.expr.*;
31 import com.github.javaparser.ast.modules.*;
32 import com.github.javaparser.ast.nodeTypes.*;
33 import com.github.javaparser.ast.stmt.*;
34 import com.github.javaparser.ast.type.*;
35 import com.github.javaparser.ast.visitor.Visitable;
36 import com.github.javaparser.ast.visitor.VoidVisitor;
37 
38 import java.util.*;
39 import java.util.concurrent.atomic.AtomicBoolean;
40 
41 import static com.github.javaparser.ast.Node.Parsedness.UNPARSABLE;
42 import static com.github.javaparser.utils.PositionUtils.sortByBeginPosition;
43 import static com.github.javaparser.utils.Utils.*;
44 import static java.util.Comparator.comparingInt;
45 import static java.util.stream.Collectors.joining;
46 
47 /**
48  * Outputs the AST as formatted Java source code.
49  *
50  * @author Julio Vilmar Gesser
51  */
52 public class PrettyPrintVisitor implements VoidVisitor<Void> {
53     protected final PrettyPrinterConfiguration configuration;
54     protected final SourcePrinter printer;
55 
PrettyPrintVisitor(PrettyPrinterConfiguration prettyPrinterConfiguration)56     public PrettyPrintVisitor(PrettyPrinterConfiguration prettyPrinterConfiguration) {
57         configuration = prettyPrinterConfiguration;
58         printer = new SourcePrinter(configuration);
59     }
60 
61     /**
62      * @deprecated use toString()
63      */
64     @Deprecated
getSource()65     public String getSource() {
66         return printer.toString();
67     }
68 
69     @Override
toString()70     public String toString() {
71         return printer.toString();
72     }
73 
printModifiers(final NodeList<Modifier> modifiers)74     private void printModifiers(final NodeList<Modifier> modifiers) {
75         if (modifiers.size() > 0) {
76             printer.print(modifiers.stream().map(Modifier::getKeyword).map(Modifier.Keyword::asString).collect(joining(" ")) + " ");
77         }
78     }
79 
printMembers(final NodeList<BodyDeclaration<?>> members, final Void arg)80     private void printMembers(final NodeList<BodyDeclaration<?>> members, final Void arg) {
81         for (final BodyDeclaration<?> member : members) {
82             printer.println();
83             member.accept(this, arg);
84             printer.println();
85         }
86     }
87 
printMemberAnnotations(final NodeList<AnnotationExpr> annotations, final Void arg)88     private void printMemberAnnotations(final NodeList<AnnotationExpr> annotations, final Void arg) {
89         if (annotations.isEmpty()) {
90             return;
91         }
92         for (final AnnotationExpr a : annotations) {
93             a.accept(this, arg);
94             printer.println();
95         }
96     }
97 
printAnnotations(final NodeList<AnnotationExpr> annotations, boolean prefixWithASpace, final Void arg)98     private void printAnnotations(final NodeList<AnnotationExpr> annotations, boolean prefixWithASpace,
99                                   final Void arg) {
100         if (annotations.isEmpty()) {
101             return;
102         }
103         if (prefixWithASpace) {
104             printer.print(" ");
105         }
106         for (AnnotationExpr annotation : annotations) {
107             annotation.accept(this, arg);
108             printer.print(" ");
109         }
110     }
111 
printTypeArgs(final NodeWithTypeArguments<?> nodeWithTypeArguments, final Void arg)112     private void printTypeArgs(final NodeWithTypeArguments<?> nodeWithTypeArguments, final Void arg) {
113         NodeList<Type> typeArguments = nodeWithTypeArguments.getTypeArguments().orElse(null);
114         if (!isNullOrEmpty(typeArguments)) {
115             printer.print("<");
116             for (final Iterator<Type> i = typeArguments.iterator(); i.hasNext(); ) {
117                 final Type t = i.next();
118                 t.accept(this, arg);
119                 if (i.hasNext()) {
120                     printer.print(", ");
121                 }
122             }
123             printer.print(">");
124         }
125     }
126 
printTypeParameters(final NodeList<TypeParameter> args, final Void arg)127     private void printTypeParameters(final NodeList<TypeParameter> args, final Void arg) {
128         if (!isNullOrEmpty(args)) {
129             printer.print("<");
130             for (final Iterator<TypeParameter> i = args.iterator(); i.hasNext(); ) {
131                 final TypeParameter t = i.next();
132                 t.accept(this, arg);
133                 if (i.hasNext()) {
134                     printer.print(", ");
135                 }
136             }
137             printer.print(">");
138         }
139     }
140 
printArguments(final NodeList<Expression> args, final Void arg)141     private void printArguments(final NodeList<Expression> args, final Void arg) {
142         printer.print("(");
143         if (!isNullOrEmpty(args)) {
144             boolean columnAlignParameters = (args.size() > 1) && configuration.isColumnAlignParameters();
145             if (columnAlignParameters) {
146                 printer.indentWithAlignTo(printer.getCursor().column);
147             }
148             for (final Iterator<Expression> i = args.iterator(); i.hasNext(); ) {
149                 final Expression e = i.next();
150                 e.accept(this, arg);
151                 if (i.hasNext()) {
152                     printer.print(",");
153                     if (columnAlignParameters) {
154                         printer.println();
155                     } else {
156                         printer.print(" ");
157                     }
158                 }
159             }
160             if (columnAlignParameters) {
161                 printer.unindent();
162             }
163         }
164         printer.print(")");
165     }
166 
printPrePostFixOptionalList(final NodeList<? extends Visitable> args, final Void arg, String prefix, String separator, String postfix)167     private void printPrePostFixOptionalList(final NodeList<? extends Visitable> args, final Void arg, String prefix, String separator, String postfix) {
168         if (!args.isEmpty()) {
169             printer.print(prefix);
170             for (final Iterator<? extends Visitable> i = args.iterator(); i.hasNext(); ) {
171                 final Visitable v = i.next();
172                 v.accept(this, arg);
173                 if (i.hasNext()) {
174                     printer.print(separator);
175                 }
176             }
177             printer.print(postfix);
178         }
179     }
180 
printPrePostFixRequiredList(final NodeList<? extends Visitable> args, final Void arg, String prefix, String separator, String postfix)181     private void printPrePostFixRequiredList(final NodeList<? extends Visitable> args, final Void arg, String prefix, String separator, String postfix) {
182         printer.print(prefix);
183         if (!args.isEmpty()) {
184             for (final Iterator<? extends Visitable> i = args.iterator(); i.hasNext(); ) {
185                 final Visitable v = i.next();
186                 v.accept(this, arg);
187                 if (i.hasNext()) {
188                     printer.print(separator);
189                 }
190             }
191         }
192         printer.print(postfix);
193     }
194 
printComment(final Optional<Comment> comment, final Void arg)195     private void printComment(final Optional<Comment> comment, final Void arg) {
196         comment.ifPresent(c -> c.accept(this, arg));
197     }
198 
199     @Override
visit(final CompilationUnit n, final Void arg)200     public void visit(final CompilationUnit n, final Void arg) {
201         printComment(n.getComment(), arg);
202         if (n.getParsed() == UNPARSABLE) {
203             printer.println("???");
204             return;
205         }
206 
207         if (n.getPackageDeclaration().isPresent()) {
208             n.getPackageDeclaration().get().accept(this, arg);
209         }
210 
211         n.getImports().accept(this, arg);
212         if (!n.getImports().isEmpty()) {
213             printer.println();
214         }
215 
216         for (final Iterator<TypeDeclaration<?>> i = n.getTypes().iterator(); i.hasNext(); ) {
217             i.next().accept(this, arg);
218             printer.println();
219             if (i.hasNext()) {
220                 printer.println();
221             }
222         }
223 
224         n.getModule().ifPresent(m -> m.accept(this, arg));
225 
226         printOrphanCommentsEnding(n);
227     }
228 
229     @Override
visit(final PackageDeclaration n, final Void arg)230     public void visit(final PackageDeclaration n, final Void arg) {
231         printComment(n.getComment(), arg);
232         printMemberAnnotations(n.getAnnotations(), arg);
233         printer.print("package ");
234         n.getName().accept(this, arg);
235         printer.println(";");
236         printer.println();
237 
238         printOrphanCommentsEnding(n);
239     }
240 
241     @Override
visit(final NameExpr n, final Void arg)242     public void visit(final NameExpr n, final Void arg) {
243         printComment(n.getComment(), arg);
244         n.getName().accept(this, arg);
245 
246         printOrphanCommentsEnding(n);
247     }
248 
249     @Override
visit(final Name n, final Void arg)250     public void visit(final Name n, final Void arg) {
251         printComment(n.getComment(), arg);
252         if (n.getQualifier().isPresent()) {
253             n.getQualifier().get().accept(this, arg);
254             printer.print(".");
255         }
256         printer.print(n.getIdentifier());
257 
258         printOrphanCommentsEnding(n);
259     }
260 
261     @Override
visit(SimpleName n, Void arg)262     public void visit(SimpleName n, Void arg) {
263         printer.print(n.getIdentifier());
264     }
265 
266     @Override
visit(final ClassOrInterfaceDeclaration n, final Void arg)267     public void visit(final ClassOrInterfaceDeclaration n, final Void arg) {
268         printComment(n.getComment(), arg);
269         printMemberAnnotations(n.getAnnotations(), arg);
270         printModifiers(n.getModifiers());
271 
272         if (n.isInterface()) {
273             printer.print("interface ");
274         } else {
275             printer.print("class ");
276         }
277 
278         n.getName().accept(this, arg);
279 
280         printTypeParameters(n.getTypeParameters(), arg);
281 
282         if (!n.getExtendedTypes().isEmpty()) {
283             printer.print(" extends ");
284             for (final Iterator<ClassOrInterfaceType> i = n.getExtendedTypes().iterator(); i.hasNext(); ) {
285                 final ClassOrInterfaceType c = i.next();
286                 c.accept(this, arg);
287                 if (i.hasNext()) {
288                     printer.print(", ");
289                 }
290             }
291         }
292 
293         if (!n.getImplementedTypes().isEmpty()) {
294             printer.print(" implements ");
295             for (final Iterator<ClassOrInterfaceType> i = n.getImplementedTypes().iterator(); i.hasNext(); ) {
296                 final ClassOrInterfaceType c = i.next();
297                 c.accept(this, arg);
298                 if (i.hasNext()) {
299                     printer.print(", ");
300                 }
301             }
302         }
303 
304         printer.println(" {");
305         printer.indent();
306         if (!isNullOrEmpty(n.getMembers())) {
307             printMembers(n.getMembers(), arg);
308         }
309 
310         printOrphanCommentsEnding(n);
311 
312         printer.unindent();
313         printer.print("}");
314     }
315 
316     @Override
visit(final JavadocComment n, final Void arg)317     public void visit(final JavadocComment n, final Void arg) {
318         if (configuration.isPrintComments() && configuration.isPrintJavadoc()) {
319             printer.println("/**");
320             final String commentContent = normalizeEolInTextBlock(n.getContent(), configuration.getEndOfLineCharacter());
321             String[] lines = commentContent.split("\\R");
322             List<String> strippedLines = new ArrayList<>();
323             for (String line : lines) {
324                 final String trimmedLine = line.trim();
325                 if (trimmedLine.startsWith("*")) {
326                     line = trimmedLine.substring(1);
327                 }
328                 line = trimTrailingSpaces(line);
329                 strippedLines.add(line);
330             }
331 
332             boolean skippingLeadingEmptyLines = true;
333             boolean prependEmptyLine = false;
334             boolean prependSpace = strippedLines.stream().anyMatch(line -> !line.isEmpty() && !line.startsWith(" "));
335             for (String line : strippedLines) {
336                 if (line.isEmpty()) {
337                     if (!skippingLeadingEmptyLines) {
338                         prependEmptyLine = true;
339                     }
340                 } else {
341                     skippingLeadingEmptyLines = false;
342                     if (prependEmptyLine) {
343                         printer.println(" *");
344                         prependEmptyLine = false;
345                     }
346                     printer.print(" *");
347                     if (prependSpace) {
348                         printer.print(" ");
349                     }
350                     printer.println(line);
351                 }
352             }
353             printer.println(" */");
354         }
355     }
356 
357     @Override
visit(final ClassOrInterfaceType n, final Void arg)358     public void visit(final ClassOrInterfaceType n, final Void arg) {
359         printComment(n.getComment(), arg);
360         if (n.getScope().isPresent()) {
361             n.getScope().get().accept(this, arg);
362             printer.print(".");
363         }
364         printAnnotations(n.getAnnotations(), false, arg);
365 
366         n.getName().accept(this, arg);
367 
368         if (n.isUsingDiamondOperator()) {
369             printer.print("<>");
370         } else {
371             printTypeArgs(n, arg);
372         }
373     }
374 
375     @Override
visit(final TypeParameter n, final Void arg)376     public void visit(final TypeParameter n, final Void arg) {
377         printComment(n.getComment(), arg);
378         printAnnotations(n.getAnnotations(), false, arg);
379         n.getName().accept(this, arg);
380         if (!isNullOrEmpty(n.getTypeBound())) {
381             printer.print(" extends ");
382             for (final Iterator<ClassOrInterfaceType> i = n.getTypeBound().iterator(); i.hasNext(); ) {
383                 final ClassOrInterfaceType c = i.next();
384                 c.accept(this, arg);
385                 if (i.hasNext()) {
386                     printer.print(" & ");
387                 }
388             }
389         }
390     }
391 
392     @Override
visit(final PrimitiveType n, final Void arg)393     public void visit(final PrimitiveType n, final Void arg) {
394         printComment(n.getComment(), arg);
395         printAnnotations(n.getAnnotations(), true, arg);
396         printer.print(n.getType().asString());
397     }
398 
399     @Override
visit(final ArrayType n, final Void arg)400     public void visit(final ArrayType n, final Void arg) {
401         final List<ArrayType> arrayTypeBuffer = new LinkedList<>();
402         Type type = n;
403         while (type instanceof ArrayType) {
404             final ArrayType arrayType = (ArrayType) type;
405             arrayTypeBuffer.add(arrayType);
406             type = arrayType.getComponentType();
407         }
408 
409         type.accept(this, arg);
410         for (ArrayType arrayType : arrayTypeBuffer) {
411             printAnnotations(arrayType.getAnnotations(), true, arg);
412             printer.print("[]");
413         }
414     }
415 
416     @Override
visit(final ArrayCreationLevel n, final Void arg)417     public void visit(final ArrayCreationLevel n, final Void arg) {
418         printAnnotations(n.getAnnotations(), true, arg);
419         printer.print("[");
420         if (n.getDimension().isPresent()) {
421             n.getDimension().get().accept(this, arg);
422         }
423         printer.print("]");
424     }
425 
426     @Override
visit(final IntersectionType n, final Void arg)427     public void visit(final IntersectionType n, final Void arg) {
428         printComment(n.getComment(), arg);
429         printAnnotations(n.getAnnotations(), false, arg);
430         boolean isFirst = true;
431         for (ReferenceType element : n.getElements()) {
432             if (isFirst) {
433                 isFirst = false;
434             } else {
435                 printer.print(" & ");
436             }
437             element.accept(this, arg);
438         }
439     }
440 
441     @Override
visit(final UnionType n, final Void arg)442     public void visit(final UnionType n, final Void arg) {
443         printComment(n.getComment(), arg);
444         printAnnotations(n.getAnnotations(), true, arg);
445         boolean isFirst = true;
446         for (ReferenceType element : n.getElements()) {
447             if (isFirst) {
448                 isFirst = false;
449             } else {
450                 printer.print(" | ");
451             }
452             element.accept(this, arg);
453         }
454     }
455 
456     @Override
visit(final WildcardType n, final Void arg)457     public void visit(final WildcardType n, final Void arg) {
458         printComment(n.getComment(), arg);
459         printAnnotations(n.getAnnotations(), false, arg);
460         printer.print("?");
461         if (n.getExtendedType().isPresent()) {
462             printer.print(" extends ");
463             n.getExtendedType().get().accept(this, arg);
464         }
465         if (n.getSuperType().isPresent()) {
466             printer.print(" super ");
467             n.getSuperType().get().accept(this, arg);
468         }
469     }
470 
471     @Override
visit(final UnknownType n, final Void arg)472     public void visit(final UnknownType n, final Void arg) {
473         // Nothing to print
474     }
475 
476     @Override
visit(final FieldDeclaration n, final Void arg)477     public void visit(final FieldDeclaration n, final Void arg) {
478         printOrphanCommentsBeforeThisChildNode(n);
479 
480         printComment(n.getComment(), arg);
481         printMemberAnnotations(n.getAnnotations(), arg);
482         printModifiers(n.getModifiers());
483         if (!n.getVariables().isEmpty()) {
484             Optional<Type> maximumCommonType = n.getMaximumCommonType();
485             maximumCommonType.ifPresent(t -> t.accept(this, arg));
486             if (!maximumCommonType.isPresent()) {
487                 printer.print("???");
488             }
489         }
490 
491         printer.print(" ");
492         for (final Iterator<VariableDeclarator> i = n.getVariables().iterator(); i.hasNext(); ) {
493             final VariableDeclarator var = i.next();
494             var.accept(this, arg);
495             if (i.hasNext()) {
496                 printer.print(", ");
497             }
498         }
499 
500         printer.print(";");
501     }
502 
503     @Override
visit(final VariableDeclarator n, final Void arg)504     public void visit(final VariableDeclarator n, final Void arg) {
505         printComment(n.getComment(), arg);
506         n.getName().accept(this, arg);
507 
508         n.findAncestor(NodeWithVariables.class).ifPresent(ancestor -> ((NodeWithVariables<?>) ancestor).getMaximumCommonType().ifPresent(commonType -> {
509 
510             final Type type = n.getType();
511 
512             ArrayType arrayType = null;
513 
514             for (int i = commonType.getArrayLevel(); i < type.getArrayLevel(); i++) {
515                 if (arrayType == null) {
516                     arrayType = (ArrayType) type;
517                 } else {
518                     arrayType = (ArrayType) arrayType.getComponentType();
519                 }
520                 printAnnotations(arrayType.getAnnotations(), true, arg);
521                 printer.print("[]");
522             }
523         }));
524 
525         if (n.getInitializer().isPresent()) {
526             printer.print(" = ");
527             n.getInitializer().get().accept(this, arg);
528         }
529     }
530 
531     @Override
visit(final ArrayInitializerExpr n, final Void arg)532     public void visit(final ArrayInitializerExpr n, final Void arg) {
533         printComment(n.getComment(), arg);
534         printer.print("{");
535         if (!isNullOrEmpty(n.getValues())) {
536             printer.print(" ");
537             for (final Iterator<Expression> i = n.getValues().iterator(); i.hasNext(); ) {
538                 final Expression expr = i.next();
539                 expr.accept(this, arg);
540                 if (i.hasNext()) {
541                     printer.print(", ");
542                 }
543             }
544             printer.print(" ");
545         }
546         printOrphanCommentsEnding(n);
547         printer.print("}");
548     }
549 
550     @Override
visit(final VoidType n, final Void arg)551     public void visit(final VoidType n, final Void arg) {
552         printComment(n.getComment(), arg);
553         printAnnotations(n.getAnnotations(), false, arg);
554         printer.print("void");
555     }
556 
557     @Override
visit(final VarType n, final Void arg)558     public void visit(final VarType n, final Void arg) {
559         printComment(n.getComment(), arg);
560         printAnnotations(n.getAnnotations(), false, arg);
561         printer.print("var");
562     }
563 
564     @Override
visit(Modifier n, Void arg)565     public void visit(Modifier n, Void arg) {
566         printer.print(n.getKeyword().asString());
567         printer.print(" ");
568     }
569 
570     @Override
visit(final ArrayAccessExpr n, final Void arg)571     public void visit(final ArrayAccessExpr n, final Void arg) {
572         printComment(n.getComment(), arg);
573         n.getName().accept(this, arg);
574         printer.print("[");
575         n.getIndex().accept(this, arg);
576         printer.print("]");
577     }
578 
579     @Override
visit(final ArrayCreationExpr n, final Void arg)580     public void visit(final ArrayCreationExpr n, final Void arg) {
581         printComment(n.getComment(), arg);
582         printer.print("new ");
583         n.getElementType().accept(this, arg);
584         for (ArrayCreationLevel level : n.getLevels()) {
585             level.accept(this, arg);
586         }
587         if (n.getInitializer().isPresent()) {
588             printer.print(" ");
589             n.getInitializer().get().accept(this, arg);
590         }
591     }
592 
593     @Override
visit(final AssignExpr n, final Void arg)594     public void visit(final AssignExpr n, final Void arg) {
595         printComment(n.getComment(), arg);
596         n.getTarget().accept(this, arg);
597         printer.print(" ");
598         printer.print(n.getOperator().asString());
599         printer.print(" ");
600         n.getValue().accept(this, arg);
601     }
602 
603     @Override
visit(final BinaryExpr n, final Void arg)604     public void visit(final BinaryExpr n, final Void arg) {
605         printComment(n.getComment(), arg);
606         n.getLeft().accept(this, arg);
607         printer.print(" ");
608         printer.print(n.getOperator().asString());
609         printer.print(" ");
610         n.getRight().accept(this, arg);
611     }
612 
613     @Override
visit(final CastExpr n, final Void arg)614     public void visit(final CastExpr n, final Void arg) {
615         printComment(n.getComment(), arg);
616         printer.print("(");
617         n.getType().accept(this, arg);
618         printer.print(") ");
619         n.getExpression().accept(this, arg);
620     }
621 
622     @Override
visit(final ClassExpr n, final Void arg)623     public void visit(final ClassExpr n, final Void arg) {
624         printComment(n.getComment(), arg);
625         n.getType().accept(this, arg);
626         printer.print(".class");
627     }
628 
629     @Override
visit(final ConditionalExpr n, final Void arg)630     public void visit(final ConditionalExpr n, final Void arg) {
631         printComment(n.getComment(), arg);
632         n.getCondition().accept(this, arg);
633         printer.print(" ? ");
634         n.getThenExpr().accept(this, arg);
635         printer.print(" : ");
636         n.getElseExpr().accept(this, arg);
637     }
638 
639     @Override
visit(final EnclosedExpr n, final Void arg)640     public void visit(final EnclosedExpr n, final Void arg) {
641         printComment(n.getComment(), arg);
642         printer.print("(");
643         n.getInner().accept(this, arg);
644         printer.print(")");
645     }
646 
647     @Override
visit(final FieldAccessExpr n, final Void arg)648     public void visit(final FieldAccessExpr n, final Void arg) {
649         printComment(n.getComment(), arg);
650         n.getScope().accept(this, arg);
651         printer.print(".");
652         n.getName().accept(this, arg);
653     }
654 
655     @Override
visit(final InstanceOfExpr n, final Void arg)656     public void visit(final InstanceOfExpr n, final Void arg) {
657         printComment(n.getComment(), arg);
658         n.getExpression().accept(this, arg);
659         printer.print(" instanceof ");
660         n.getType().accept(this, arg);
661     }
662 
663     @Override
visit(final CharLiteralExpr n, final Void arg)664     public void visit(final CharLiteralExpr n, final Void arg) {
665         printComment(n.getComment(), arg);
666         printer.print("'");
667         printer.print(n.getValue());
668         printer.print("'");
669     }
670 
671     @Override
visit(final DoubleLiteralExpr n, final Void arg)672     public void visit(final DoubleLiteralExpr n, final Void arg) {
673         printComment(n.getComment(), arg);
674         printer.print(n.getValue());
675     }
676 
677     @Override
visit(final IntegerLiteralExpr n, final Void arg)678     public void visit(final IntegerLiteralExpr n, final Void arg) {
679         printComment(n.getComment(), arg);
680         printer.print(n.getValue());
681     }
682 
683     @Override
visit(final LongLiteralExpr n, final Void arg)684     public void visit(final LongLiteralExpr n, final Void arg) {
685         printComment(n.getComment(), arg);
686         printer.print(n.getValue());
687     }
688 
689     @Override
visit(final StringLiteralExpr n, final Void arg)690     public void visit(final StringLiteralExpr n, final Void arg) {
691         printComment(n.getComment(), arg);
692         printer.print("\"");
693         printer.print(n.getValue());
694         printer.print("\"");
695     }
696 
697     @Override
visit(final BooleanLiteralExpr n, final Void arg)698     public void visit(final BooleanLiteralExpr n, final Void arg) {
699         printComment(n.getComment(), arg);
700         printer.print(String.valueOf(n.getValue()));
701     }
702 
703     @Override
visit(final NullLiteralExpr n, final Void arg)704     public void visit(final NullLiteralExpr n, final Void arg) {
705         printComment(n.getComment(), arg);
706         printer.print("null");
707     }
708 
709     @Override
visit(final ThisExpr n, final Void arg)710     public void visit(final ThisExpr n, final Void arg) {
711         printComment(n.getComment(), arg);
712         if (n.getTypeName().isPresent()) {
713             n.getTypeName().get().accept(this, arg);
714             printer.print(".");
715         }
716         printer.print("this");
717     }
718 
719     @Override
visit(final SuperExpr n, final Void arg)720     public void visit(final SuperExpr n, final Void arg) {
721         printComment(n.getComment(), arg);
722         if (n.getTypeName().isPresent()) {
723             n.getTypeName().get().accept(this, arg);
724             printer.print(".");
725         }
726         printer.print("super");
727     }
728 
729     @Override
visit(final MethodCallExpr n, final Void arg)730     public void visit(final MethodCallExpr n, final Void arg) {
731         printComment(n.getComment(), arg);
732 
733         // determine whether we do reindenting for aligmnent at all
734         // - is it enabled?
735         // - are we in a statement where we want the alignment?
736         // - are we not directly in the argument list of a method call expression?
737         AtomicBoolean columnAlignFirstMethodChain = new AtomicBoolean();
738         if (configuration.isColumnAlignFirstMethodChain()) {
739             // pick the kind of expressions where vertically aligning method calls is okay.
740             if (n.findAncestor(Statement.class).map(p -> p.isReturnStmt()
741                     || p.isThrowStmt()
742                     || p.isAssertStmt()
743                     || p.isExpressionStmt()).orElse(false)) {
744                 // search for first parent that does not have its child as scope
745                 Node c = n;
746                 Optional<Node> p = c.getParentNode();
747                 while (p.isPresent() && p.filter(NodeWithTraversableScope.class::isInstance)
748                         .map(NodeWithTraversableScope.class::cast)
749                         .flatMap(NodeWithTraversableScope::traverseScope)
750                         .map(c::equals)
751                         .orElse(false)) {
752                     c = p.get();
753                     p = c.getParentNode();
754                 }
755 
756                 // check if the parent is a method call and thus we are in an argument list
757                 columnAlignFirstMethodChain.set(!p.filter(MethodCallExpr.class::isInstance).isPresent());
758             }
759         }
760 
761         // we are at the last method call of a call chain
762         // this means we do not start reindenting for alignment or we undo it
763         AtomicBoolean lastMethodInCallChain = new AtomicBoolean(true);
764         if (columnAlignFirstMethodChain.get()) {
765             Node node = n;
766             while (node.getParentNode()
767                     .filter(NodeWithTraversableScope.class::isInstance)
768                     .map(NodeWithTraversableScope.class::cast)
769                     .flatMap(NodeWithTraversableScope::traverseScope)
770                     .map(node::equals)
771                     .orElse(false)) {
772                 node = node.getParentNode().orElseThrow(AssertionError::new);
773                 if (node instanceof MethodCallExpr) {
774                     lastMethodInCallChain.set(false);
775                     break;
776                 }
777             }
778         }
779 
780         // search whether there is a method call with scope in the scope already
781         // this means that we probably started reindenting for alignment there
782         AtomicBoolean methodCallWithScopeInScope = new AtomicBoolean();
783         if (columnAlignFirstMethodChain.get()) {
784             Optional<Expression> s = n.getScope();
785             while (s.filter(NodeWithTraversableScope.class::isInstance).isPresent()) {
786                 Optional<Expression> parentScope = s.map(NodeWithTraversableScope.class::cast)
787                         .flatMap(NodeWithTraversableScope::traverseScope);
788                 if (s.filter(MethodCallExpr.class::isInstance).isPresent() && parentScope.isPresent()) {
789                     methodCallWithScopeInScope.set(true);
790                     break;
791                 }
792                 s = parentScope;
793             }
794         }
795 
796         // we have a scope
797         // this means we are not the first method in the chain
798         n.getScope().ifPresent(scope -> {
799             scope.accept(this, arg);
800             if (columnAlignFirstMethodChain.get()) {
801                 if (methodCallWithScopeInScope.get()) {
802                     /* We're a method call on the result of something (method call, property access, ...) that is not stand alone,
803                        and not the first one with scope, like:
804                        we're x() in a.b().x(), or in a=b().c[15].d.e().x().
805                        That means that the "else" has been executed by one of the methods in the scope chain, so that the alignment
806                        is set to the "." of that method.
807                        That means we will align to that "." when we start a new line: */
808                     printer.println();
809                 } else if (!lastMethodInCallChain.get()) {
810                     /* We're the first method call on the result of something in the chain (method call, property access, ...),
811                        but we are not at the same time the last method call in that chain, like:
812                        we're x() in a().x().y(), or in Long.x().y.z(). That means we get to dictate the indent of following method
813                        calls in this chain by setting the cursor to where we are now: just before the "."
814                        that start this method call. */
815                     printer.reindentWithAlignToCursor();
816                 }
817             }
818             printer.print(".");
819         });
820 
821         printTypeArgs(n, arg);
822         n.getName().accept(this, arg);
823         printer.duplicateIndent();
824         printArguments(n.getArguments(), arg);
825         printer.unindent();
826         if (columnAlignFirstMethodChain.get() && methodCallWithScopeInScope.get() && lastMethodInCallChain.get()) {
827             // undo the aligning after the arguments of the last method call are printed
828             printer.reindentToPreviousLevel();
829         }
830     }
831 
832     @Override
visit(final ObjectCreationExpr n, final Void arg)833     public void visit(final ObjectCreationExpr n, final Void arg) {
834         printComment(n.getComment(), arg);
835         if (n.getScope().isPresent()) {
836             n.getScope().get().accept(this, arg);
837             printer.print(".");
838         }
839 
840         printer.print("new ");
841 
842         printTypeArgs(n, arg);
843         if (!isNullOrEmpty(n.getTypeArguments().orElse(null))) {
844             printer.print(" ");
845         }
846 
847         n.getType().accept(this, arg);
848 
849         printArguments(n.getArguments(), arg);
850 
851         if (n.getAnonymousClassBody().isPresent()) {
852             printer.println(" {");
853             printer.indent();
854             printMembers(n.getAnonymousClassBody().get(), arg);
855             printer.unindent();
856             printer.print("}");
857         }
858     }
859 
860     @Override
visit(final UnaryExpr n, final Void arg)861     public void visit(final UnaryExpr n, final Void arg) {
862         printComment(n.getComment(), arg);
863         if (n.getOperator().isPrefix()) {
864             printer.print(n.getOperator().asString());
865         }
866 
867         n.getExpression().accept(this, arg);
868 
869         if (n.getOperator().isPostfix()) {
870             printer.print(n.getOperator().asString());
871         }
872     }
873 
874     @Override
visit(final ConstructorDeclaration n, final Void arg)875     public void visit(final ConstructorDeclaration n, final Void arg) {
876         printComment(n.getComment(), arg);
877         printMemberAnnotations(n.getAnnotations(), arg);
878         printModifiers(n.getModifiers());
879 
880         printTypeParameters(n.getTypeParameters(), arg);
881         if (n.isGeneric()) {
882             printer.print(" ");
883         }
884         n.getName().accept(this, arg);
885 
886         printer.print("(");
887         if (!n.getParameters().isEmpty()) {
888             for (final Iterator<Parameter> i = n.getParameters().iterator(); i.hasNext(); ) {
889                 final Parameter p = i.next();
890                 p.accept(this, arg);
891                 if (i.hasNext()) {
892                     printer.print(", ");
893                 }
894             }
895         }
896         printer.print(")");
897 
898         if (!isNullOrEmpty(n.getThrownExceptions())) {
899             printer.print(" throws ");
900             for (final Iterator<ReferenceType> i = n.getThrownExceptions().iterator(); i.hasNext(); ) {
901                 final ReferenceType name = i.next();
902                 name.accept(this, arg);
903                 if (i.hasNext()) {
904                     printer.print(", ");
905                 }
906             }
907         }
908         printer.print(" ");
909         n.getBody().accept(this, arg);
910     }
911 
912     @Override
visit(final MethodDeclaration n, final Void arg)913     public void visit(final MethodDeclaration n, final Void arg) {
914         printOrphanCommentsBeforeThisChildNode(n);
915 
916         printComment(n.getComment(), arg);
917         printMemberAnnotations(n.getAnnotations(), arg);
918         printModifiers(n.getModifiers());
919         printTypeParameters(n.getTypeParameters(), arg);
920         if (!isNullOrEmpty(n.getTypeParameters())) {
921             printer.print(" ");
922         }
923 
924         n.getType().accept(this, arg);
925         printer.print(" ");
926         n.getName().accept(this, arg);
927 
928         printer.print("(");
929         n.getReceiverParameter().ifPresent(rp -> {
930             rp.accept(this, arg);
931             if (!isNullOrEmpty(n.getParameters())) {
932                 printer.print(", ");
933             }
934         });
935         if (!isNullOrEmpty(n.getParameters())) {
936             for (final Iterator<Parameter> i = n.getParameters().iterator(); i.hasNext(); ) {
937                 final Parameter p = i.next();
938                 p.accept(this, arg);
939                 if (i.hasNext()) {
940                     printer.print(", ");
941                 }
942             }
943         }
944         printer.print(")");
945 
946         if (!isNullOrEmpty(n.getThrownExceptions())) {
947             printer.print(" throws ");
948             for (final Iterator<ReferenceType> i = n.getThrownExceptions().iterator(); i.hasNext(); ) {
949                 final ReferenceType name = i.next();
950                 name.accept(this, arg);
951                 if (i.hasNext()) {
952                     printer.print(", ");
953                 }
954             }
955         }
956         if (!n.getBody().isPresent()) {
957             printer.print(";");
958         } else {
959             printer.print(" ");
960             n.getBody().get().accept(this, arg);
961         }
962     }
963 
964     @Override
visit(final Parameter n, final Void arg)965     public void visit(final Parameter n, final Void arg) {
966         printComment(n.getComment(), arg);
967         printAnnotations(n.getAnnotations(), false, arg);
968         printModifiers(n.getModifiers());
969         n.getType().accept(this, arg);
970         if (n.isVarArgs()) {
971             printAnnotations(n.getVarArgsAnnotations(), false, arg);
972             printer.print("...");
973         }
974         if (!(n.getType() instanceof UnknownType)) {
975             printer.print(" ");
976         }
977         n.getName().accept(this, arg);
978     }
979 
980     @Override
visit(final ReceiverParameter n, final Void arg)981     public void visit(final ReceiverParameter n, final Void arg) {
982         printComment(n.getComment(), arg);
983         printAnnotations(n.getAnnotations(), false, arg);
984         n.getType().accept(this, arg);
985         printer.print(" ");
986         n.getName().accept(this, arg);
987     }
988 
989     @Override
visit(final ExplicitConstructorInvocationStmt n, final Void arg)990     public void visit(final ExplicitConstructorInvocationStmt n, final Void arg) {
991         printComment(n.getComment(), arg);
992         if (n.isThis()) {
993             printTypeArgs(n, arg);
994             printer.print("this");
995         } else {
996             if (n.getExpression().isPresent()) {
997                 n.getExpression().get().accept(this, arg);
998                 printer.print(".");
999             }
1000             printTypeArgs(n, arg);
1001             printer.print("super");
1002         }
1003         printArguments(n.getArguments(), arg);
1004         printer.print(";");
1005     }
1006 
1007     @Override
visit(final VariableDeclarationExpr n, final Void arg)1008     public void visit(final VariableDeclarationExpr n, final Void arg) {
1009         printComment(n.getComment(), arg);
1010         if (n.getParentNode().map(ExpressionStmt.class::isInstance).orElse(false)) {
1011             printMemberAnnotations(n.getAnnotations(), arg);
1012         } else {
1013             printAnnotations(n.getAnnotations(), false, arg);
1014         }
1015         printModifiers(n.getModifiers());
1016 
1017         if (!n.getVariables().isEmpty()) {
1018             n.getMaximumCommonType().ifPresent(t -> t.accept(this, arg));
1019         }
1020         printer.print(" ");
1021 
1022         for (final Iterator<VariableDeclarator> i = n.getVariables().iterator(); i.hasNext(); ) {
1023             final VariableDeclarator v = i.next();
1024             v.accept(this, arg);
1025             if (i.hasNext()) {
1026                 printer.print(", ");
1027             }
1028         }
1029     }
1030 
1031     @Override
visit(final LocalClassDeclarationStmt n, final Void arg)1032     public void visit(final LocalClassDeclarationStmt n, final Void arg) {
1033         printComment(n.getComment(), arg);
1034         n.getClassDeclaration().accept(this, arg);
1035     }
1036 
1037     @Override
visit(final AssertStmt n, final Void arg)1038     public void visit(final AssertStmt n, final Void arg) {
1039         printComment(n.getComment(), arg);
1040         printer.print("assert ");
1041         n.getCheck().accept(this, arg);
1042         if (n.getMessage().isPresent()) {
1043             printer.print(" : ");
1044             n.getMessage().get().accept(this, arg);
1045         }
1046         printer.print(";");
1047     }
1048 
1049     @Override
visit(final BlockStmt n, final Void arg)1050     public void visit(final BlockStmt n, final Void arg) {
1051         printOrphanCommentsBeforeThisChildNode(n);
1052         printComment(n.getComment(), arg);
1053         printer.println("{");
1054         if (n.getStatements() != null) {
1055             printer.indent();
1056             for (final Statement s : n.getStatements()) {
1057                 s.accept(this, arg);
1058                 printer.println();
1059             }
1060             printer.unindent();
1061         }
1062         printOrphanCommentsEnding(n);
1063         printer.print("}");
1064     }
1065 
1066     @Override
visit(final LabeledStmt n, final Void arg)1067     public void visit(final LabeledStmt n, final Void arg) {
1068         printComment(n.getComment(), arg);
1069         n.getLabel().accept(this, arg);
1070         printer.print(": ");
1071         n.getStatement().accept(this, arg);
1072     }
1073 
1074     @Override
visit(final EmptyStmt n, final Void arg)1075     public void visit(final EmptyStmt n, final Void arg) {
1076         printComment(n.getComment(), arg);
1077         printer.print(";");
1078     }
1079 
1080     @Override
visit(final ExpressionStmt n, final Void arg)1081     public void visit(final ExpressionStmt n, final Void arg) {
1082         printOrphanCommentsBeforeThisChildNode(n);
1083         printComment(n.getComment(), arg);
1084         n.getExpression().accept(this, arg);
1085         printer.print(";");
1086     }
1087 
1088     @Override
visit(final SwitchStmt n, final Void arg)1089     public void visit(final SwitchStmt n, final Void arg) {
1090         printSwitchNode(n, arg);
1091     }
1092 
1093     @Override
visit(SwitchExpr n, Void arg)1094     public void visit(SwitchExpr n, Void arg) {
1095         printSwitchNode(n, arg);
1096     }
1097 
printSwitchNode(SwitchNode n, Void arg)1098     private void printSwitchNode(SwitchNode n, Void arg) {
1099         printComment(n.getComment(), arg);
1100         printer.print("switch(");
1101         n.getSelector().accept(this, arg);
1102         printer.println(") {");
1103         if (n.getEntries() != null) {
1104             printer.indent();
1105             for (final SwitchEntry e : n.getEntries()) {
1106                 e.accept(this, arg);
1107             }
1108             printer.unindent();
1109         }
1110         printer.print("}");
1111     }
1112 
1113     @Override
visit(final SwitchEntry n, final Void arg)1114     public void visit(final SwitchEntry n, final Void arg) {
1115         printComment(n.getComment(), arg);
1116 
1117         if (isNullOrEmpty(n.getLabels())) {
1118             printer.print("default:");
1119         } else {
1120             printer.print("case ");
1121             for (final Iterator<Expression> i = n.getLabels().iterator(); i.hasNext(); ) {
1122                 final Expression label = i.next();
1123                 label.accept(this, arg);
1124                 if (i.hasNext()) {
1125                     printer.print(", ");
1126                 }
1127             }
1128             printer.print(":");
1129         }
1130         printer.println();
1131         printer.indent();
1132         if (n.getStatements() != null) {
1133             for (final Statement s : n.getStatements()) {
1134                 s.accept(this, arg);
1135                 printer.println();
1136             }
1137         }
1138         printer.unindent();
1139     }
1140 
1141     @Override
visit(final BreakStmt n, final Void arg)1142     public void visit(final BreakStmt n, final Void arg) {
1143         printComment(n.getComment(), arg);
1144         printer.print("break");
1145         n.getValue().ifPresent(value -> {
1146             printer.print(" ");
1147             value.accept(this, arg);
1148         });
1149         printer.print(";");
1150     }
1151 
1152     @Override
visit(final ReturnStmt n, final Void arg)1153     public void visit(final ReturnStmt n, final Void arg) {
1154         printComment(n.getComment(), arg);
1155         printer.print("return");
1156         if (n.getExpression().isPresent()) {
1157             printer.print(" ");
1158             n.getExpression().get().accept(this, arg);
1159         }
1160         printer.print(";");
1161     }
1162 
1163     @Override
visit(final EnumDeclaration n, final Void arg)1164     public void visit(final EnumDeclaration n, final Void arg) {
1165         printComment(n.getComment(), arg);
1166         printMemberAnnotations(n.getAnnotations(), arg);
1167         printModifiers(n.getModifiers());
1168 
1169         printer.print("enum ");
1170         n.getName().accept(this, arg);
1171 
1172         if (!n.getImplementedTypes().isEmpty()) {
1173             printer.print(" implements ");
1174             for (final Iterator<ClassOrInterfaceType> i = n.getImplementedTypes().iterator(); i.hasNext(); ) {
1175                 final ClassOrInterfaceType c = i.next();
1176                 c.accept(this, arg);
1177                 if (i.hasNext()) {
1178                     printer.print(", ");
1179                 }
1180             }
1181         }
1182 
1183         printer.println(" {");
1184         printer.indent();
1185         if (n.getEntries().isNonEmpty()) {
1186             final boolean alignVertically =
1187                     // Either we hit the constant amount limit in the configurations, or...
1188                     n.getEntries().size() > configuration.getMaxEnumConstantsToAlignHorizontally() ||
1189                             // any of the constants has a comment.
1190                             n.getEntries().stream().anyMatch(e -> e.getComment().isPresent());
1191             printer.println();
1192             for (final Iterator<EnumConstantDeclaration> i = n.getEntries().iterator(); i.hasNext(); ) {
1193                 final EnumConstantDeclaration e = i.next();
1194                 e.accept(this, arg);
1195                 if (i.hasNext()) {
1196                     if (alignVertically) {
1197                         printer.println(",");
1198                     } else {
1199                         printer.print(", ");
1200                     }
1201                 }
1202             }
1203         }
1204         if (!n.getMembers().isEmpty()) {
1205             printer.println(";");
1206             printMembers(n.getMembers(), arg);
1207         } else {
1208             if (!n.getEntries().isEmpty()) {
1209                 printer.println();
1210             }
1211         }
1212         printer.unindent();
1213         printer.print("}");
1214     }
1215 
1216     @Override
visit(final EnumConstantDeclaration n, final Void arg)1217     public void visit(final EnumConstantDeclaration n, final Void arg) {
1218         printComment(n.getComment(), arg);
1219         printMemberAnnotations(n.getAnnotations(), arg);
1220         n.getName().accept(this, arg);
1221 
1222         if (!n.getArguments().isEmpty()) {
1223             printArguments(n.getArguments(), arg);
1224         }
1225 
1226         if (!n.getClassBody().isEmpty()) {
1227             printer.println(" {");
1228             printer.indent();
1229             printMembers(n.getClassBody(), arg);
1230             printer.unindent();
1231             printer.println("}");
1232         }
1233     }
1234 
1235     @Override
visit(final InitializerDeclaration n, final Void arg)1236     public void visit(final InitializerDeclaration n, final Void arg) {
1237         printComment(n.getComment(), arg);
1238         if (n.isStatic()) {
1239             printer.print("static ");
1240         }
1241         n.getBody().accept(this, arg);
1242     }
1243 
1244     @Override
visit(final IfStmt n, final Void arg)1245     public void visit(final IfStmt n, final Void arg) {
1246         printComment(n.getComment(), arg);
1247         printer.print("if (");
1248         n.getCondition().accept(this, arg);
1249         final boolean thenBlock = n.getThenStmt() instanceof BlockStmt;
1250         if (thenBlock) // block statement should start on the same line
1251             printer.print(") ");
1252         else {
1253             printer.println(")");
1254             printer.indent();
1255         }
1256         n.getThenStmt().accept(this, arg);
1257         if (!thenBlock)
1258             printer.unindent();
1259         if (n.getElseStmt().isPresent()) {
1260             if (thenBlock)
1261                 printer.print(" ");
1262             else
1263                 printer.println();
1264             final boolean elseIf = n.getElseStmt().orElse(null) instanceof IfStmt;
1265             final boolean elseBlock = n.getElseStmt().orElse(null) instanceof BlockStmt;
1266             if (elseIf || elseBlock) // put chained if and start of block statement on a same level
1267                 printer.print("else ");
1268             else {
1269                 printer.println("else");
1270                 printer.indent();
1271             }
1272             if (n.getElseStmt().isPresent())
1273                 n.getElseStmt().get().accept(this, arg);
1274             if (!(elseIf || elseBlock))
1275                 printer.unindent();
1276         }
1277     }
1278 
1279     @Override
visit(final WhileStmt n, final Void arg)1280     public void visit(final WhileStmt n, final Void arg) {
1281         printComment(n.getComment(), arg);
1282         printer.print("while (");
1283         n.getCondition().accept(this, arg);
1284         printer.print(") ");
1285         n.getBody().accept(this, arg);
1286     }
1287 
1288     @Override
visit(final ContinueStmt n, final Void arg)1289     public void visit(final ContinueStmt n, final Void arg) {
1290         printComment(n.getComment(), arg);
1291         printer.print("continue");
1292         n.getLabel().ifPresent(l -> printer.print(" ").print(l.getIdentifier()));
1293         printer.print(";");
1294     }
1295 
1296     @Override
visit(final DoStmt n, final Void arg)1297     public void visit(final DoStmt n, final Void arg) {
1298         printComment(n.getComment(), arg);
1299         printer.print("do ");
1300         n.getBody().accept(this, arg);
1301         printer.print(" while (");
1302         n.getCondition().accept(this, arg);
1303         printer.print(");");
1304     }
1305 
1306     @Override
visit(final ForEachStmt n, final Void arg)1307     public void visit(final ForEachStmt n, final Void arg) {
1308         printComment(n.getComment(), arg);
1309         printer.print("for (");
1310         n.getVariable().accept(this, arg);
1311         printer.print(" : ");
1312         n.getIterable().accept(this, arg);
1313         printer.print(") ");
1314         n.getBody().accept(this, arg);
1315     }
1316 
1317     @Override
visit(final ForStmt n, final Void arg)1318     public void visit(final ForStmt n, final Void arg) {
1319         printComment(n.getComment(), arg);
1320         printer.print("for (");
1321         if (n.getInitialization() != null) {
1322             for (final Iterator<Expression> i = n.getInitialization().iterator(); i.hasNext(); ) {
1323                 final Expression e = i.next();
1324                 e.accept(this, arg);
1325                 if (i.hasNext()) {
1326                     printer.print(", ");
1327                 }
1328             }
1329         }
1330         printer.print("; ");
1331         if (n.getCompare().isPresent()) {
1332             n.getCompare().get().accept(this, arg);
1333         }
1334         printer.print("; ");
1335         if (n.getUpdate() != null) {
1336             for (final Iterator<Expression> i = n.getUpdate().iterator(); i.hasNext(); ) {
1337                 final Expression e = i.next();
1338                 e.accept(this, arg);
1339                 if (i.hasNext()) {
1340                     printer.print(", ");
1341                 }
1342             }
1343         }
1344         printer.print(") ");
1345         n.getBody().accept(this, arg);
1346     }
1347 
1348     @Override
visit(final ThrowStmt n, final Void arg)1349     public void visit(final ThrowStmt n, final Void arg) {
1350         printComment(n.getComment(), arg);
1351         printer.print("throw ");
1352         n.getExpression().accept(this, arg);
1353         printer.print(";");
1354     }
1355 
1356     @Override
visit(final SynchronizedStmt n, final Void arg)1357     public void visit(final SynchronizedStmt n, final Void arg) {
1358         printComment(n.getComment(), arg);
1359         printer.print("synchronized (");
1360         n.getExpression().accept(this, arg);
1361         printer.print(") ");
1362         n.getBody().accept(this, arg);
1363     }
1364 
1365     @Override
visit(final TryStmt n, final Void arg)1366     public void visit(final TryStmt n, final Void arg) {
1367         printComment(n.getComment(), arg);
1368         printer.print("try ");
1369         if (!n.getResources().isEmpty()) {
1370             printer.print("(");
1371             Iterator<Expression> resources = n.getResources().iterator();
1372             boolean first = true;
1373             while (resources.hasNext()) {
1374                 resources.next().accept(this, arg);
1375                 if (resources.hasNext()) {
1376                     printer.print(";");
1377                     printer.println();
1378                     if (first) {
1379                         printer.indent();
1380                     }
1381                 }
1382                 first = false;
1383             }
1384             if (n.getResources().size() > 1) {
1385                 printer.unindent();
1386             }
1387             printer.print(") ");
1388         }
1389         n.getTryBlock().accept(this, arg);
1390         for (final CatchClause c : n.getCatchClauses()) {
1391             c.accept(this, arg);
1392         }
1393         if (n.getFinallyBlock().isPresent()) {
1394             printer.print(" finally ");
1395             n.getFinallyBlock().get().accept(this, arg);
1396         }
1397     }
1398 
1399     @Override
visit(final CatchClause n, final Void arg)1400     public void visit(final CatchClause n, final Void arg) {
1401         printComment(n.getComment(), arg);
1402         printer.print(" catch (");
1403         n.getParameter().accept(this, arg);
1404         printer.print(") ");
1405         n.getBody().accept(this, arg);
1406     }
1407 
1408     @Override
visit(final AnnotationDeclaration n, final Void arg)1409     public void visit(final AnnotationDeclaration n, final Void arg) {
1410         printComment(n.getComment(), arg);
1411         printMemberAnnotations(n.getAnnotations(), arg);
1412         printModifiers(n.getModifiers());
1413 
1414         printer.print("@interface ");
1415         n.getName().accept(this, arg);
1416         printer.println(" {");
1417         printer.indent();
1418         if (n.getMembers() != null) {
1419             printMembers(n.getMembers(), arg);
1420         }
1421         printer.unindent();
1422         printer.print("}");
1423     }
1424 
1425     @Override
visit(final AnnotationMemberDeclaration n, final Void arg)1426     public void visit(final AnnotationMemberDeclaration n, final Void arg) {
1427         printComment(n.getComment(), arg);
1428         printMemberAnnotations(n.getAnnotations(), arg);
1429         printModifiers(n.getModifiers());
1430 
1431         n.getType().accept(this, arg);
1432         printer.print(" ");
1433         n.getName().accept(this, arg);
1434         printer.print("()");
1435         if (n.getDefaultValue().isPresent()) {
1436             printer.print(" default ");
1437             n.getDefaultValue().get().accept(this, arg);
1438         }
1439         printer.print(";");
1440     }
1441 
1442     @Override
visit(final MarkerAnnotationExpr n, final Void arg)1443     public void visit(final MarkerAnnotationExpr n, final Void arg) {
1444         printComment(n.getComment(), arg);
1445         printer.print("@");
1446         n.getName().accept(this, arg);
1447     }
1448 
1449     @Override
visit(final SingleMemberAnnotationExpr n, final Void arg)1450     public void visit(final SingleMemberAnnotationExpr n, final Void arg) {
1451         printComment(n.getComment(), arg);
1452         printer.print("@");
1453         n.getName().accept(this, arg);
1454         printer.print("(");
1455         n.getMemberValue().accept(this, arg);
1456         printer.print(")");
1457     }
1458 
1459     @Override
visit(final NormalAnnotationExpr n, final Void arg)1460     public void visit(final NormalAnnotationExpr n, final Void arg) {
1461         printComment(n.getComment(), arg);
1462         printer.print("@");
1463         n.getName().accept(this, arg);
1464         printer.print("(");
1465         if (n.getPairs() != null) {
1466             for (final Iterator<MemberValuePair> i = n.getPairs().iterator(); i.hasNext(); ) {
1467                 final MemberValuePair m = i.next();
1468                 m.accept(this, arg);
1469                 if (i.hasNext()) {
1470                     printer.print(", ");
1471                 }
1472             }
1473         }
1474         printer.print(")");
1475     }
1476 
1477     @Override
visit(final MemberValuePair n, final Void arg)1478     public void visit(final MemberValuePair n, final Void arg) {
1479         printComment(n.getComment(), arg);
1480         n.getName().accept(this, arg);
1481         printer.print(" = ");
1482         n.getValue().accept(this, arg);
1483     }
1484 
1485     @Override
visit(final LineComment n, final Void arg)1486     public void visit(final LineComment n, final Void arg) {
1487         if (configuration.isIgnoreComments()) {
1488             return;
1489         }
1490         printer
1491                 .print("// ")
1492                 .println(normalizeEolInTextBlock(n.getContent(), "").trim());
1493     }
1494 
1495     @Override
visit(final BlockComment n, final Void arg)1496     public void visit(final BlockComment n, final Void arg) {
1497         if (configuration.isIgnoreComments()) {
1498             return;
1499         }
1500         final String commentContent = normalizeEolInTextBlock(n.getContent(), configuration.getEndOfLineCharacter());
1501         String[] lines = commentContent.split("\\R", -1); // as BlockComment should not be formatted, -1 to preserve any trailing empty line if present
1502         printer.print("/*");
1503         for (int i = 0; i < (lines.length - 1); i++) {
1504             printer.print(lines[i]);
1505             printer.print(configuration.getEndOfLineCharacter()); // Avoids introducing indentation in blockcomments. ie: do not use println() as it would trigger indentation at the next print call.
1506         }
1507         printer.print(lines[lines.length - 1]); // last line is not followed by a newline, and simply terminated with `*/`
1508         printer.println("*/");
1509     }
1510 
1511     @Override
visit(LambdaExpr n, Void arg)1512     public void visit(LambdaExpr n, Void arg) {
1513         printComment(n.getComment(), arg);
1514 
1515         final NodeList<Parameter> parameters = n.getParameters();
1516         final boolean printPar = n.isEnclosingParameters();
1517 
1518         if (printPar) {
1519             printer.print("(");
1520         }
1521         for (Iterator<Parameter> i = parameters.iterator(); i.hasNext(); ) {
1522             Parameter p = i.next();
1523             p.accept(this, arg);
1524             if (i.hasNext()) {
1525                 printer.print(", ");
1526             }
1527         }
1528         if (printPar) {
1529             printer.print(")");
1530         }
1531 
1532         printer.print(" -> ");
1533         final Statement body = n.getBody();
1534         if (body instanceof ExpressionStmt) {
1535             // Print the expression directly
1536             ((ExpressionStmt) body).getExpression().accept(this, arg);
1537         } else {
1538             body.accept(this, arg);
1539         }
1540     }
1541 
1542     @Override
visit(MethodReferenceExpr n, Void arg)1543     public void visit(MethodReferenceExpr n, Void arg) {
1544         printComment(n.getComment(), arg);
1545         Expression scope = n.getScope();
1546         String identifier = n.getIdentifier();
1547         if (scope != null) {
1548             n.getScope().accept(this, arg);
1549         }
1550 
1551         printer.print("::");
1552         printTypeArgs(n, arg);
1553         if (identifier != null) {
1554             printer.print(identifier);
1555         }
1556     }
1557 
1558     @Override
visit(TypeExpr n, Void arg)1559     public void visit(TypeExpr n, Void arg) {
1560         printComment(n.getComment(), arg);
1561         if (n.getType() != null) {
1562             n.getType().accept(this, arg);
1563         }
1564     }
1565 
1566     @Override
visit(NodeList n, Void arg)1567     public void visit(NodeList n, Void arg) {
1568         if (configuration.isOrderImports() && n.size() > 0 && n.get(0) instanceof ImportDeclaration) {
1569             //noinspection unchecked
1570             NodeList<ImportDeclaration> modifiableList = new NodeList<>(n);
1571             modifiableList.sort(
1572                     comparingInt((ImportDeclaration i) -> i.isStatic() ? 0 : 1)
1573                             .thenComparing(NodeWithName::getNameAsString));
1574             for (Object node : modifiableList) {
1575                 ((Node) node).accept(this, arg);
1576             }
1577         } else {
1578             for (Object node : n) {
1579                 ((Node) node).accept(this, arg);
1580             }
1581         }
1582     }
1583 
1584     @Override
visit(final ImportDeclaration n, final Void arg)1585     public void visit(final ImportDeclaration n, final Void arg) {
1586         printComment(n.getComment(), arg);
1587         printer.print("import ");
1588         if (n.isStatic()) {
1589             printer.print("static ");
1590         }
1591         n.getName().accept(this, arg);
1592         if (n.isAsterisk()) {
1593             printer.print(".*");
1594         }
1595         printer.println(";");
1596 
1597         printOrphanCommentsEnding(n);
1598     }
1599 
1600 
1601     @Override
visit(ModuleDeclaration n, Void arg)1602     public void visit(ModuleDeclaration n, Void arg) {
1603         printMemberAnnotations(n.getAnnotations(), arg);
1604         if (n.isOpen()) {
1605             printer.print("open ");
1606         }
1607         printer.print("module ");
1608         n.getName().accept(this, arg);
1609         printer.println(" {").indent();
1610         n.getDirectives().accept(this, arg);
1611         printer.unindent().println("}");
1612     }
1613 
1614     @Override
visit(ModuleRequiresDirective n, Void arg)1615     public void visit(ModuleRequiresDirective n, Void arg) {
1616         printer.print("requires ");
1617         printModifiers(n.getModifiers());
1618         n.getName().accept(this, arg);
1619         printer.println(";");
1620     }
1621 
1622     @Override
visit(ModuleExportsDirective n, Void arg)1623     public void visit(ModuleExportsDirective n, Void arg) {
1624         printer.print("exports ");
1625         n.getName().accept(this, arg);
1626         printPrePostFixOptionalList(n.getModuleNames(), arg, " to ", ", ", "");
1627         printer.println(";");
1628     }
1629 
1630     @Override
visit(ModuleProvidesDirective n, Void arg)1631     public void visit(ModuleProvidesDirective n, Void arg) {
1632         printer.print("provides ");
1633         n.getName().accept(this, arg);
1634         printPrePostFixRequiredList(n.getWith(), arg, " with ", ", ", "");
1635         printer.println(";");
1636     }
1637 
1638     @Override
visit(ModuleUsesDirective n, Void arg)1639     public void visit(ModuleUsesDirective n, Void arg) {
1640         printer.print("uses ");
1641         n.getName().accept(this, arg);
1642         printer.println(";");
1643     }
1644 
1645     @Override
visit(ModuleOpensDirective n, Void arg)1646     public void visit(ModuleOpensDirective n, Void arg) {
1647         printer.print("opens ");
1648         n.getName().accept(this, arg);
1649         printPrePostFixOptionalList(n.getModuleNames(), arg, " to ", ", ", "");
1650         printer.println(";");
1651     }
1652 
1653     @Override
visit(UnparsableStmt n, Void arg)1654     public void visit(UnparsableStmt n, Void arg) {
1655         printer.print("???;");
1656     }
1657 
printOrphanCommentsBeforeThisChildNode(final Node node)1658     private void printOrphanCommentsBeforeThisChildNode(final Node node) {
1659         if (configuration.isIgnoreComments()) return;
1660         if (node instanceof Comment) return;
1661 
1662         Node parent = node.getParentNode().orElse(null);
1663         if (parent == null) return;
1664         List<Node> everything = new ArrayList<>(parent.getChildNodes());
1665         sortByBeginPosition(everything);
1666         int positionOfTheChild = -1;
1667         for (int i = 0; i < everything.size(); ++i) { // indexOf is by equality, so this is used to index by identity
1668             if (everything.get(i) == node) {
1669                 positionOfTheChild = i;
1670                 break;
1671             }
1672         }
1673         if (positionOfTheChild == -1) {
1674             throw new AssertionError("I am not a child of my parent.");
1675         }
1676         int positionOfPreviousChild = -1;
1677         for (int i = positionOfTheChild - 1; i >= 0 && positionOfPreviousChild == -1; i--) {
1678             if (!(everything.get(i) instanceof Comment)) positionOfPreviousChild = i;
1679         }
1680         for (int i = positionOfPreviousChild + 1; i < positionOfTheChild; i++) {
1681             Node nodeToPrint = everything.get(i);
1682             if (!(nodeToPrint instanceof Comment))
1683                 throw new RuntimeException(
1684                         "Expected comment, instead " + nodeToPrint.getClass() + ". Position of previous child: "
1685                                 + positionOfPreviousChild + ", position of child " + positionOfTheChild);
1686             nodeToPrint.accept(this, null);
1687         }
1688     }
1689 
printOrphanCommentsEnding(final Node node)1690     private void printOrphanCommentsEnding(final Node node) {
1691         if (configuration.isIgnoreComments()) return;
1692 
1693         List<Node> everything = new ArrayList<>(node.getChildNodes());
1694         sortByBeginPosition(everything);
1695         if (everything.isEmpty()) {
1696             return;
1697         }
1698 
1699         int commentsAtEnd = 0;
1700         boolean findingComments = true;
1701         while (findingComments && commentsAtEnd < everything.size()) {
1702             Node last = everything.get(everything.size() - 1 - commentsAtEnd);
1703             findingComments = (last instanceof Comment);
1704             if (findingComments) {
1705                 commentsAtEnd++;
1706             }
1707         }
1708         for (int i = 0; i < commentsAtEnd; i++) {
1709             everything.get(everything.size() - commentsAtEnd + i).accept(this, null);
1710         }
1711     }
1712 
1713 }
1714