1/*
2 * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
3 * Copyright (C) 2011, 2013-2015 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
22package com.github.javaparser.ast.visitor;
23
24import static com.github.javaparser.PositionUtils.sortByBeginPosition;
25import com.github.javaparser.ast.CompilationUnit;
26import com.github.javaparser.ast.ImportDeclaration;
27import com.github.javaparser.ast.Node;
28import com.github.javaparser.ast.PackageDeclaration;
29import com.github.javaparser.ast.TypeParameter;
30import com.github.javaparser.ast.body.AnnotationDeclaration;
31import com.github.javaparser.ast.body.AnnotationMemberDeclaration;
32import com.github.javaparser.ast.body.BodyDeclaration;
33import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
34import com.github.javaparser.ast.body.ConstructorDeclaration;
35import com.github.javaparser.ast.body.EmptyMemberDeclaration;
36import com.github.javaparser.ast.body.EmptyTypeDeclaration;
37import com.github.javaparser.ast.body.EnumConstantDeclaration;
38import com.github.javaparser.ast.body.EnumDeclaration;
39import com.github.javaparser.ast.body.FieldDeclaration;
40import com.github.javaparser.ast.body.InitializerDeclaration;
41import com.github.javaparser.ast.body.MethodDeclaration;
42import com.github.javaparser.ast.body.ModifierSet;
43import com.github.javaparser.ast.body.MultiTypeParameter;
44import com.github.javaparser.ast.body.Parameter;
45import com.github.javaparser.ast.body.TypeDeclaration;
46import com.github.javaparser.ast.body.VariableDeclarator;
47import com.github.javaparser.ast.body.VariableDeclaratorId;
48import com.github.javaparser.ast.comments.BlockComment;
49import com.github.javaparser.ast.comments.Comment;
50import com.github.javaparser.ast.comments.JavadocComment;
51import com.github.javaparser.ast.comments.LineComment;
52import com.github.javaparser.ast.expr.*;
53import com.github.javaparser.ast.stmt.AssertStmt;
54import com.github.javaparser.ast.stmt.BlockStmt;
55import com.github.javaparser.ast.stmt.BreakStmt;
56import com.github.javaparser.ast.stmt.CatchClause;
57import com.github.javaparser.ast.stmt.ContinueStmt;
58import com.github.javaparser.ast.stmt.DoStmt;
59import com.github.javaparser.ast.stmt.EmptyStmt;
60import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
61import com.github.javaparser.ast.stmt.ExpressionStmt;
62import com.github.javaparser.ast.stmt.ForStmt;
63import com.github.javaparser.ast.stmt.ForeachStmt;
64import com.github.javaparser.ast.stmt.IfStmt;
65import com.github.javaparser.ast.stmt.LabeledStmt;
66import com.github.javaparser.ast.stmt.ReturnStmt;
67import com.github.javaparser.ast.stmt.Statement;
68import com.github.javaparser.ast.stmt.SwitchEntryStmt;
69import com.github.javaparser.ast.stmt.SwitchStmt;
70import com.github.javaparser.ast.stmt.SynchronizedStmt;
71import com.github.javaparser.ast.stmt.ThrowStmt;
72import com.github.javaparser.ast.stmt.TryStmt;
73import com.github.javaparser.ast.stmt.TypeDeclarationStmt;
74import com.github.javaparser.ast.stmt.WhileStmt;
75import com.github.javaparser.ast.type.*;
76
77import java.util.Iterator;
78import java.util.LinkedList;
79import java.util.List;
80
81import static com.github.javaparser.ast.internal.Utils.isNullOrEmpty;
82
83/**
84 * Dumps the AST to formatted Java source code.
85 *
86 * @author Julio Vilmar Gesser
87 */
88public final class DumpVisitor implements VoidVisitor<Object> {
89
90    private boolean printComments;
91
92    public DumpVisitor() {
93        this(true);
94    }
95
96    public DumpVisitor(boolean printComments) {
97        this.printComments = printComments;
98    }
99
100	private static class SourcePrinter {
101
102		private int level = 0;
103
104		private boolean indented = false;
105
106		private final StringBuilder buf = new StringBuilder();
107
108		public void indent() {
109			level++;
110		}
111
112		public void unindent() {
113			level--;
114		}
115
116		private void makeIndent() {
117			for (int i = 0; i < level; i++) {
118				buf.append("    ");
119			}
120		}
121
122		public void print(final String arg) {
123			if (!indented) {
124				makeIndent();
125				indented = true;
126			}
127			buf.append(arg);
128		}
129
130		public void printLn(final String arg) {
131			print(arg);
132			printLn();
133		}
134
135		public void printLn() {
136			buf.append(System.getProperty("line.separator"));
137			indented = false;
138		}
139
140		public String getSource() {
141			return buf.toString();
142		}
143
144		@Override public String toString() {
145			return getSource();
146		}
147	}
148
149	private final SourcePrinter printer = new SourcePrinter();
150
151	public String getSource() {
152		return printer.getSource();
153	}
154
155	private void printModifiers(final int modifiers) {
156		if (ModifierSet.isPrivate(modifiers)) {
157			printer.print("private ");
158		}
159		if (ModifierSet.isProtected(modifiers)) {
160			printer.print("protected ");
161		}
162		if (ModifierSet.isPublic(modifiers)) {
163			printer.print("public ");
164		}
165		if (ModifierSet.isAbstract(modifiers)) {
166			printer.print("abstract ");
167		}
168		if (ModifierSet.isStatic(modifiers)) {
169			printer.print("static ");
170		}
171		if (ModifierSet.isFinal(modifiers)) {
172			printer.print("final ");
173		}
174		if (ModifierSet.isNative(modifiers)) {
175			printer.print("native ");
176		}
177		if (ModifierSet.isStrictfp(modifiers)) {
178			printer.print("strictfp ");
179		}
180		if (ModifierSet.isSynchronized(modifiers)) {
181			printer.print("synchronized ");
182		}
183		if (ModifierSet.isTransient(modifiers)) {
184			printer.print("transient ");
185		}
186		if (ModifierSet.isVolatile(modifiers)) {
187			printer.print("volatile ");
188		}
189	}
190
191	private void printMembers(final List<BodyDeclaration> members, final Object arg) {
192		for (final BodyDeclaration member : members) {
193			printer.printLn();
194			member.accept(this, arg);
195			printer.printLn();
196		}
197	}
198
199	private void printMemberAnnotations(final List<AnnotationExpr> annotations, final Object arg) {
200		if (!isNullOrEmpty(annotations)) {
201			for (final AnnotationExpr a : annotations) {
202				a.accept(this, arg);
203				printer.printLn();
204			}
205		}
206	}
207
208	private void printAnnotations(final List<AnnotationExpr> annotations, final Object arg) {
209		if (!isNullOrEmpty(annotations)) {
210			for (final AnnotationExpr a : annotations) {
211				a.accept(this, arg);
212				printer.print(" ");
213			}
214		}
215	}
216
217	private void printTypeArgs(final List<Type> args, final Object arg) {
218        if (!isNullOrEmpty(args)) {
219			printer.print("<");
220			for (final Iterator<Type> i = args.iterator(); i.hasNext();) {
221				final Type t = i.next();
222				t.accept(this, arg);
223				if (i.hasNext()) {
224					printer.print(", ");
225				}
226			}
227			printer.print(">");
228		}
229	}
230
231	private void printTypeParameters(final List<TypeParameter> args, final Object arg) {
232        if (!isNullOrEmpty(args)) {
233			printer.print("<");
234			for (final Iterator<TypeParameter> i = args.iterator(); i.hasNext();) {
235				final TypeParameter t = i.next();
236				t.accept(this, arg);
237				if (i.hasNext()) {
238					printer.print(", ");
239				}
240			}
241			printer.print(">");
242		}
243	}
244
245	private void printArguments(final List<Expression> args, final Object arg) {
246		printer.print("(");
247        if (!isNullOrEmpty(args)) {
248			for (final Iterator<Expression> i = args.iterator(); i.hasNext();) {
249				final Expression e = i.next();
250				e.accept(this, arg);
251				if (i.hasNext()) {
252					printer.print(", ");
253				}
254			}
255		}
256		printer.print(")");
257	}
258
259	private void printJavadoc(final JavadocComment javadoc, final Object arg) {
260		if (javadoc != null) {
261			javadoc.accept(this, arg);
262		}
263	}
264
265	private void printJavaComment(final Comment javacomment, final Object arg) {
266		if (javacomment != null) {
267			javacomment.accept(this, arg);
268		}
269	}
270
271	@Override public void visit(final CompilationUnit n, final Object arg) {
272		printJavaComment(n.getComment(), arg);
273
274		if (n.getPackage() != null) {
275			n.getPackage().accept(this, arg);
276		}
277
278		if (n.getImports() != null) {
279			for (final ImportDeclaration i : n.getImports()) {
280				i.accept(this, arg);
281			}
282			printer.printLn();
283		}
284
285		if (n.getTypes() != null) {
286			for (final Iterator<TypeDeclaration> i = n.getTypes().iterator(); i.hasNext();) {
287				i.next().accept(this, arg);
288				printer.printLn();
289				if (i.hasNext()) {
290					printer.printLn();
291				}
292			}
293		}
294
295        printOrphanCommentsEnding(n);
296	}
297
298	@Override public void visit(final PackageDeclaration n, final Object arg) {
299		printJavaComment(n.getComment(), arg);
300		printAnnotations(n.getAnnotations(), arg);
301		printer.print("package ");
302		n.getName().accept(this, arg);
303		printer.printLn(";");
304		printer.printLn();
305
306        printOrphanCommentsEnding(n);
307	}
308
309	@Override public void visit(final NameExpr n, final Object arg) {
310		printJavaComment(n.getComment(), arg);
311		printer.print(n.getName());
312
313        printOrphanCommentsEnding(n);
314	}
315
316	@Override public void visit(final QualifiedNameExpr n, final Object arg) {
317		printJavaComment(n.getComment(), arg);
318		n.getQualifier().accept(this, arg);
319		printer.print(".");
320		printer.print(n.getName());
321
322        printOrphanCommentsEnding(n);
323	}
324
325	@Override public void visit(final ImportDeclaration n, final Object arg) {
326		printJavaComment(n.getComment(), arg);
327		printer.print("import ");
328		if (n.isStatic()) {
329			printer.print("static ");
330		}
331		n.getName().accept(this, arg);
332		if (n.isAsterisk()) {
333			printer.print(".*");
334		}
335		printer.printLn(";");
336
337        printOrphanCommentsEnding(n);
338	}
339
340	@Override public void visit(final ClassOrInterfaceDeclaration n, final Object arg) {
341		printJavaComment(n.getComment(), arg);
342		printJavadoc(n.getJavaDoc(), arg);
343		printMemberAnnotations(n.getAnnotations(), arg);
344		printModifiers(n.getModifiers());
345
346		if (n.isInterface()) {
347			printer.print("interface ");
348		} else {
349			printer.print("class ");
350		}
351
352		printer.print(n.getName());
353
354		printTypeParameters(n.getTypeParameters(), arg);
355
356		if (!isNullOrEmpty(n.getExtends())) {
357			printer.print(" extends ");
358			for (final Iterator<ClassOrInterfaceType> i = n.getExtends().iterator(); i.hasNext();) {
359				final ClassOrInterfaceType c = i.next();
360				c.accept(this, arg);
361				if (i.hasNext()) {
362					printer.print(", ");
363				}
364			}
365		}
366
367		if (!isNullOrEmpty(n.getImplements())) {
368			printer.print(" implements ");
369			for (final Iterator<ClassOrInterfaceType> i = n.getImplements().iterator(); i.hasNext();) {
370				final ClassOrInterfaceType c = i.next();
371				c.accept(this, arg);
372				if (i.hasNext()) {
373					printer.print(", ");
374				}
375			}
376		}
377
378		printer.printLn(" {");
379		printer.indent();
380		if (!isNullOrEmpty(n.getMembers())) {
381			printMembers(n.getMembers(), arg);
382		}
383
384        printOrphanCommentsEnding(n);
385
386		printer.unindent();
387		printer.print("}");
388	}
389
390	@Override public void visit(final EmptyTypeDeclaration n, final Object arg) {
391		printJavaComment(n.getComment(), arg);
392		printJavadoc(n.getJavaDoc(), arg);
393		printer.print(";");
394
395        printOrphanCommentsEnding(n);
396	}
397
398	@Override public void visit(final JavadocComment n, final Object arg) {
399		printer.print("/**");
400		printer.print(n.getContent());
401		printer.printLn("*/");
402	}
403
404	@Override public void visit(final ClassOrInterfaceType n, final Object arg) {
405		printJavaComment(n.getComment(), arg);
406
407		if (n.getAnnotations() != null) {
408			for (AnnotationExpr ae : n.getAnnotations()) {
409				ae.accept(this, arg);
410				printer.print(" ");
411			}
412		}
413
414		if (n.getScope() != null) {
415			n.getScope().accept(this, arg);
416			printer.print(".");
417		}
418		printer.print(n.getName());
419		printTypeArgs(n.getTypeArgs(), arg);
420	}
421
422	@Override public void visit(final TypeParameter n, final Object arg) {
423		printJavaComment(n.getComment(), arg);
424		if (n.getAnnotations() != null) {
425			for (AnnotationExpr ann : n.getAnnotations()) {
426				ann.accept(this, arg);
427				printer.print(" ");
428			}
429		}
430		printer.print(n.getName());
431		if (n.getTypeBound() != null) {
432			printer.print(" extends ");
433			for (final Iterator<ClassOrInterfaceType> i = n.getTypeBound().iterator(); i.hasNext();) {
434				final ClassOrInterfaceType c = i.next();
435				c.accept(this, arg);
436				if (i.hasNext()) {
437					printer.print(" & ");
438				}
439			}
440		}
441	}
442
443	@Override public void visit(final PrimitiveType n, final Object arg) {
444		printJavaComment(n.getComment(), arg);
445		if (n.getAnnotations() != null) {
446			for (AnnotationExpr ae : n.getAnnotations()) {
447				ae.accept(this, arg);
448				printer.print(" ");
449			}
450		}
451		switch (n.getType()) {
452		case Boolean:
453			printer.print("boolean");
454			break;
455		case Byte:
456			printer.print("byte");
457			break;
458		case Char:
459			printer.print("char");
460			break;
461		case Double:
462			printer.print("double");
463			break;
464		case Float:
465			printer.print("float");
466			break;
467		case Int:
468			printer.print("int");
469			break;
470		case Long:
471			printer.print("long");
472			break;
473		case Short:
474			printer.print("short");
475			break;
476		}
477	}
478
479	@Override public void visit(final ReferenceType n, final Object arg) {
480		printJavaComment(n.getComment(), arg);
481		if (n.getAnnotations() != null) {
482			for (AnnotationExpr ae : n.getAnnotations()) {
483				ae.accept(this, arg);
484				printer.print(" ");
485			}
486		}
487		n.getType().accept(this, arg);
488		List<List<AnnotationExpr>> arraysAnnotations = n.getArraysAnnotations();
489		for (int i = 0; i < n.getArrayCount(); i++) {
490			if (arraysAnnotations != null && i < arraysAnnotations.size()) {
491				List<AnnotationExpr> annotations = arraysAnnotations.get(i);
492				if (annotations != null) {
493					for (AnnotationExpr ae : annotations) {
494						printer.print(" ");
495						ae.accept(this, arg);
496
497					}
498				}
499			}
500			printer.print("[]");
501		}
502	}
503
504	@Override public void visit(final WildcardType n, final Object arg) {
505		printJavaComment(n.getComment(), arg);
506		if (n.getAnnotations() != null) {
507			for (AnnotationExpr ae : n.getAnnotations()) {
508				printer.print(" ");
509				ae.accept(this, arg);
510			}
511		}
512		printer.print("?");
513		if (n.getExtends() != null) {
514			printer.print(" extends ");
515			n.getExtends().accept(this, arg);
516		}
517		if (n.getSuper() != null) {
518			printer.print(" super ");
519			n.getSuper().accept(this, arg);
520		}
521	}
522
523	@Override public void visit(final UnknownType n, final Object arg) {
524		// Nothing to dump
525	}
526
527	@Override public void visit(final FieldDeclaration n, final Object arg) {
528        printOrphanCommentsBeforeThisChildNode(n);
529
530		printJavaComment(n.getComment(), arg);
531		printJavadoc(n.getJavaDoc(), arg);
532		printMemberAnnotations(n.getAnnotations(), arg);
533		printModifiers(n.getModifiers());
534		n.getType().accept(this, arg);
535
536		printer.print(" ");
537		for (final Iterator<VariableDeclarator> i = n.getVariables().iterator(); i.hasNext();) {
538			final VariableDeclarator var = i.next();
539			var.accept(this, arg);
540			if (i.hasNext()) {
541				printer.print(", ");
542			}
543		}
544
545		printer.print(";");
546	}
547
548	@Override public void visit(final VariableDeclarator n, final Object arg) {
549		printJavaComment(n.getComment(), arg);
550		n.getId().accept(this, arg);
551		if (n.getInit() != null) {
552			printer.print(" = ");
553			n.getInit().accept(this, arg);
554		}
555	}
556
557	@Override public void visit(final VariableDeclaratorId n, final Object arg) {
558		printJavaComment(n.getComment(), arg);
559		printer.print(n.getName());
560		for (int i = 0; i < n.getArrayCount(); i++) {
561			printer.print("[]");
562		}
563	}
564
565	@Override public void visit(final ArrayInitializerExpr n, final Object arg) {
566		printJavaComment(n.getComment(), arg);
567		printer.print("{");
568		if (n.getValues() != null) {
569			printer.print(" ");
570			for (final Iterator<Expression> i = n.getValues().iterator(); i.hasNext();) {
571				final Expression expr = i.next();
572				expr.accept(this, arg);
573				if (i.hasNext()) {
574					printer.print(", ");
575				}
576			}
577			printer.print(" ");
578		}
579		printer.print("}");
580	}
581
582	@Override public void visit(final VoidType n, final Object arg) {
583		printJavaComment(n.getComment(), arg);
584		printer.print("void");
585	}
586
587	@Override public void visit(final ArrayAccessExpr n, final Object arg) {
588		printJavaComment(n.getComment(), arg);
589		n.getName().accept(this, arg);
590		printer.print("[");
591		n.getIndex().accept(this, arg);
592		printer.print("]");
593	}
594
595	@Override public void visit(final ArrayCreationExpr n, final Object arg) {
596		printJavaComment(n.getComment(), arg);
597		printer.print("new ");
598		n.getType().accept(this, arg);
599		List<List<AnnotationExpr>> arraysAnnotations = n.getArraysAnnotations();
600		if (n.getDimensions() != null) {
601			int j = 0;
602			for (final Expression dim : n.getDimensions()) {
603
604				if (arraysAnnotations != null && j < arraysAnnotations.size()) {
605					List<AnnotationExpr> annotations = arraysAnnotations.get(j);
606					if (annotations != null) {
607						for (AnnotationExpr ae : annotations) {
608							printer.print(" ");
609							ae.accept(this, arg);
610						}
611					}
612				}
613				printer.print("[");
614				dim.accept(this, arg);
615				printer.print("]");
616				j++;
617			}
618			for (int i = 0; i < n.getArrayCount(); i++) {
619				if (arraysAnnotations != null && i < arraysAnnotations.size()) {
620
621					List<AnnotationExpr> annotations = arraysAnnotations.get(i);
622					if (annotations != null) {
623						for (AnnotationExpr ae : annotations) {
624							printer.print(" ");
625							ae.accept(this, arg);
626
627						}
628					}
629				}
630				printer.print("[]");
631			}
632
633		} else {
634			for (int i = 0; i < n.getArrayCount(); i++) {
635				if (arraysAnnotations != null && i < arraysAnnotations.size()) {
636					List<AnnotationExpr> annotations = arraysAnnotations.get(i);
637					if (annotations != null) {
638						for (AnnotationExpr ae : annotations) {
639							ae.accept(this, arg);
640							printer.print(" ");
641						}
642					}
643				}
644				printer.print("[]");
645			}
646			printer.print(" ");
647			n.getInitializer().accept(this, arg);
648		}
649	}
650
651	@Override public void visit(final AssignExpr n, final Object arg) {
652		printJavaComment(n.getComment(), arg);
653		n.getTarget().accept(this, arg);
654		printer.print(" ");
655		switch (n.getOperator()) {
656		case assign:
657			printer.print("=");
658			break;
659		case and:
660			printer.print("&=");
661			break;
662		case or:
663			printer.print("|=");
664			break;
665		case xor:
666			printer.print("^=");
667			break;
668		case plus:
669			printer.print("+=");
670			break;
671		case minus:
672			printer.print("-=");
673			break;
674		case rem:
675			printer.print("%=");
676			break;
677		case slash:
678			printer.print("/=");
679			break;
680		case star:
681			printer.print("*=");
682			break;
683		case lShift:
684			printer.print("<<=");
685			break;
686		case rSignedShift:
687			printer.print(">>=");
688			break;
689		case rUnsignedShift:
690			printer.print(">>>=");
691			break;
692		}
693		printer.print(" ");
694		n.getValue().accept(this, arg);
695	}
696
697	@Override public void visit(final BinaryExpr n, final Object arg) {
698		printJavaComment(n.getComment(), arg);
699		n.getLeft().accept(this, arg);
700		printer.print(" ");
701		switch (n.getOperator()) {
702		case or:
703			printer.print("||");
704			break;
705		case and:
706			printer.print("&&");
707			break;
708		case binOr:
709			printer.print("|");
710			break;
711		case binAnd:
712			printer.print("&");
713			break;
714		case xor:
715			printer.print("^");
716			break;
717		case equals:
718			printer.print("==");
719			break;
720		case notEquals:
721			printer.print("!=");
722			break;
723		case less:
724			printer.print("<");
725			break;
726		case greater:
727			printer.print(">");
728			break;
729		case lessEquals:
730			printer.print("<=");
731			break;
732		case greaterEquals:
733			printer.print(">=");
734			break;
735		case lShift:
736			printer.print("<<");
737			break;
738		case rSignedShift:
739			printer.print(">>");
740			break;
741		case rUnsignedShift:
742			printer.print(">>>");
743			break;
744		case plus:
745			printer.print("+");
746			break;
747		case minus:
748			printer.print("-");
749			break;
750		case times:
751			printer.print("*");
752			break;
753		case divide:
754			printer.print("/");
755			break;
756		case remainder:
757			printer.print("%");
758			break;
759		}
760		printer.print(" ");
761		n.getRight().accept(this, arg);
762	}
763
764	@Override public void visit(final CastExpr n, final Object arg) {
765		printJavaComment(n.getComment(), arg);
766		printer.print("(");
767		n.getType().accept(this, arg);
768		printer.print(") ");
769		n.getExpr().accept(this, arg);
770	}
771
772	@Override public void visit(final ClassExpr n, final Object arg) {
773		printJavaComment(n.getComment(), arg);
774		n.getType().accept(this, arg);
775		printer.print(".class");
776	}
777
778	@Override public void visit(final ConditionalExpr n, final Object arg) {
779		printJavaComment(n.getComment(), arg);
780		n.getCondition().accept(this, arg);
781		printer.print(" ? ");
782		n.getThenExpr().accept(this, arg);
783		printer.print(" : ");
784		n.getElseExpr().accept(this, arg);
785	}
786
787	@Override public void visit(final EnclosedExpr n, final Object arg) {
788		printJavaComment(n.getComment(), arg);
789		printer.print("(");
790		if (n.getInner() != null) {
791		n.getInner().accept(this, arg);
792		}
793		printer.print(")");
794	}
795
796	@Override public void visit(final FieldAccessExpr n, final Object arg) {
797		printJavaComment(n.getComment(), arg);
798		n.getScope().accept(this, arg);
799		printer.print(".");
800		printer.print(n.getField());
801	}
802
803	@Override public void visit(final InstanceOfExpr n, final Object arg) {
804		printJavaComment(n.getComment(), arg);
805		n.getExpr().accept(this, arg);
806		printer.print(" instanceof ");
807		n.getType().accept(this, arg);
808	}
809
810	@Override public void visit(final CharLiteralExpr n, final Object arg) {
811		printJavaComment(n.getComment(), arg);
812		printer.print("'");
813		printer.print(n.getValue());
814		printer.print("'");
815	}
816
817	@Override public void visit(final DoubleLiteralExpr n, final Object arg) {
818		printJavaComment(n.getComment(), arg);
819		printer.print(n.getValue());
820	}
821
822	@Override public void visit(final IntegerLiteralExpr n, final Object arg) {
823		printJavaComment(n.getComment(), arg);
824		printer.print(n.getValue());
825	}
826
827	@Override public void visit(final LongLiteralExpr n, final Object arg) {
828		printJavaComment(n.getComment(), arg);
829		printer.print(n.getValue());
830	}
831
832	@Override public void visit(final IntegerLiteralMinValueExpr n, final Object arg) {
833		printJavaComment(n.getComment(), arg);
834		printer.print(n.getValue());
835	}
836
837	@Override public void visit(final LongLiteralMinValueExpr n, final Object arg) {
838		printJavaComment(n.getComment(), arg);
839		printer.print(n.getValue());
840	}
841
842	@Override public void visit(final StringLiteralExpr n, final Object arg) {
843		printJavaComment(n.getComment(), arg);
844		printer.print("\"");
845		printer.print(n.getValue());
846		printer.print("\"");
847	}
848
849	@Override public void visit(final BooleanLiteralExpr n, final Object arg) {
850		printJavaComment(n.getComment(), arg);
851		printer.print(String.valueOf(n.getValue()));
852	}
853
854	@Override public void visit(final NullLiteralExpr n, final Object arg) {
855		printJavaComment(n.getComment(), arg);
856		printer.print("null");
857	}
858
859	@Override public void visit(final ThisExpr n, final Object arg) {
860		printJavaComment(n.getComment(), arg);
861		if (n.getClassExpr() != null) {
862			n.getClassExpr().accept(this, arg);
863			printer.print(".");
864		}
865		printer.print("this");
866	}
867
868	@Override public void visit(final SuperExpr n, final Object arg) {
869		printJavaComment(n.getComment(), arg);
870		if (n.getClassExpr() != null) {
871			n.getClassExpr().accept(this, arg);
872			printer.print(".");
873		}
874		printer.print("super");
875	}
876
877	@Override public void visit(final MethodCallExpr n, final Object arg) {
878		printJavaComment(n.getComment(), arg);
879		if (n.getScope() != null) {
880			n.getScope().accept(this, arg);
881			printer.print(".");
882		}
883		printTypeArgs(n.getTypeArgs(), arg);
884		printer.print(n.getName());
885		printArguments(n.getArgs(), arg);
886	}
887
888	@Override public void visit(final ObjectCreationExpr n, final Object arg) {
889		printJavaComment(n.getComment(), arg);
890		if (n.getScope() != null) {
891			n.getScope().accept(this, arg);
892			printer.print(".");
893		}
894
895		printer.print("new ");
896
897		printTypeArgs(n.getTypeArgs(), arg);
898		if (!isNullOrEmpty(n.getTypeArgs())) {
899			printer.print(" ");
900		}
901
902		n.getType().accept(this, arg);
903
904		printArguments(n.getArgs(), arg);
905
906		if (n.getAnonymousClassBody() != null) {
907			printer.printLn(" {");
908			printer.indent();
909			printMembers(n.getAnonymousClassBody(), arg);
910			printer.unindent();
911			printer.print("}");
912		}
913	}
914
915	@Override public void visit(final UnaryExpr n, final Object arg) {
916		printJavaComment(n.getComment(), arg);
917		switch (n.getOperator()) {
918		case positive:
919			printer.print("+");
920			break;
921		case negative:
922			printer.print("-");
923			break;
924		case inverse:
925			printer.print("~");
926			break;
927		case not:
928			printer.print("!");
929			break;
930		case preIncrement:
931			printer.print("++");
932			break;
933		case preDecrement:
934			printer.print("--");
935			break;
936		default:
937		}
938
939		n.getExpr().accept(this, arg);
940
941		switch (n.getOperator()) {
942		case posIncrement:
943			printer.print("++");
944			break;
945		case posDecrement:
946			printer.print("--");
947			break;
948		default:
949		}
950	}
951
952	@Override public void visit(final ConstructorDeclaration n, final Object arg) {
953		printJavaComment(n.getComment(), arg);
954		printJavadoc(n.getJavaDoc(), arg);
955		printMemberAnnotations(n.getAnnotations(), arg);
956		printModifiers(n.getModifiers());
957
958		printTypeParameters(n.getTypeParameters(), arg);
959		if (n.getTypeParameters() != null) {
960			printer.print(" ");
961		}
962		printer.print(n.getName());
963
964		printer.print("(");
965		if (n.getParameters() != null) {
966			for (final Iterator<Parameter> i = n.getParameters().iterator(); i.hasNext();) {
967				final Parameter p = i.next();
968				p.accept(this, arg);
969				if (i.hasNext()) {
970					printer.print(", ");
971				}
972			}
973		}
974		printer.print(")");
975
976		if (!isNullOrEmpty(n.getThrows())) {
977			printer.print(" throws ");
978			for (final Iterator<NameExpr> i = n.getThrows().iterator(); i.hasNext();) {
979				final NameExpr name = i.next();
980				name.accept(this, arg);
981				if (i.hasNext()) {
982					printer.print(", ");
983				}
984			}
985		}
986		printer.print(" ");
987		n.getBlock().accept(this, arg);
988	}
989
990	@Override public void visit(final MethodDeclaration n, final Object arg) {
991        printOrphanCommentsBeforeThisChildNode(n);
992
993		printJavaComment(n.getComment(), arg);
994		printJavadoc(n.getJavaDoc(), arg);
995		printMemberAnnotations(n.getAnnotations(), arg);
996		printModifiers(n.getModifiers());
997		if (n.isDefault()) {
998			printer.print("default ");
999		}
1000		printTypeParameters(n.getTypeParameters(), arg);
1001		if (n.getTypeParameters() != null) {
1002			printer.print(" ");
1003		}
1004
1005		n.getType().accept(this, arg);
1006		printer.print(" ");
1007		printer.print(n.getName());
1008
1009		printer.print("(");
1010		if (n.getParameters() != null) {
1011			for (final Iterator<Parameter> i = n.getParameters().iterator(); i.hasNext();) {
1012				final Parameter p = i.next();
1013				p.accept(this, arg);
1014				if (i.hasNext()) {
1015					printer.print(", ");
1016				}
1017			}
1018		}
1019		printer.print(")");
1020
1021		for (int i = 0; i < n.getArrayCount(); i++) {
1022			printer.print("[]");
1023		}
1024
1025		if (!isNullOrEmpty(n.getThrows())) {
1026			printer.print(" throws ");
1027			for (final Iterator<NameExpr> i = n.getThrows().iterator(); i.hasNext();) {
1028				final NameExpr name = i.next();
1029				name.accept(this, arg);
1030				if (i.hasNext()) {
1031					printer.print(", ");
1032				}
1033			}
1034		}
1035		if (n.getBody() == null) {
1036			printer.print(";");
1037		} else {
1038			printer.print(" ");
1039			n.getBody().accept(this, arg);
1040		}
1041	}
1042
1043	@Override public void visit(final Parameter n, final Object arg) {
1044		printJavaComment(n.getComment(), arg);
1045		printAnnotations(n.getAnnotations(), arg);
1046		printModifiers(n.getModifiers());
1047		if (n.getType() != null) {
1048			n.getType().accept(this, arg);
1049		}
1050		if (n.isVarArgs()) {
1051			printer.print("...");
1052		}
1053		printer.print(" ");
1054		n.getId().accept(this, arg);
1055	}
1056
1057    @Override public void visit(MultiTypeParameter n, Object arg) {
1058        printAnnotations(n.getAnnotations(), arg);
1059        printModifiers(n.getModifiers());
1060
1061        Iterator<Type> types = n.getTypes().iterator();
1062        types.next().accept(this, arg);
1063        while (types.hasNext()) {
1064        	printer.print(" | ");
1065        	types.next().accept(this, arg);
1066        }
1067
1068        printer.print(" ");
1069        n.getId().accept(this, arg);
1070    }
1071
1072	@Override public void visit(final ExplicitConstructorInvocationStmt n, final Object arg) {
1073		printJavaComment(n.getComment(), arg);
1074		if (n.isThis()) {
1075			printTypeArgs(n.getTypeArgs(), arg);
1076			printer.print("this");
1077		} else {
1078			if (n.getExpr() != null) {
1079				n.getExpr().accept(this, arg);
1080				printer.print(".");
1081			}
1082			printTypeArgs(n.getTypeArgs(), arg);
1083			printer.print("super");
1084		}
1085		printArguments(n.getArgs(), arg);
1086		printer.print(";");
1087	}
1088
1089	@Override public void visit(final VariableDeclarationExpr n, final Object arg) {
1090		printJavaComment(n.getComment(), arg);
1091		printAnnotations(n.getAnnotations(), arg);
1092		printModifiers(n.getModifiers());
1093
1094		n.getType().accept(this, arg);
1095		printer.print(" ");
1096
1097		for (final Iterator<VariableDeclarator> i = n.getVars().iterator(); i.hasNext();) {
1098			final VariableDeclarator v = i.next();
1099			v.accept(this, arg);
1100			if (i.hasNext()) {
1101				printer.print(", ");
1102			}
1103		}
1104	}
1105
1106	@Override public void visit(final TypeDeclarationStmt n, final Object arg) {
1107		printJavaComment(n.getComment(), arg);
1108		n.getTypeDeclaration().accept(this, arg);
1109	}
1110
1111	@Override public void visit(final AssertStmt n, final Object arg) {
1112		printJavaComment(n.getComment(), arg);
1113		printer.print("assert ");
1114		n.getCheck().accept(this, arg);
1115		if (n.getMessage() != null) {
1116			printer.print(" : ");
1117			n.getMessage().accept(this, arg);
1118		}
1119		printer.print(";");
1120	}
1121
1122	@Override public void visit(final BlockStmt n, final Object arg) {
1123        printOrphanCommentsBeforeThisChildNode(n);
1124		printJavaComment(n.getComment(), arg);
1125		printer.printLn("{");
1126		if (n.getStmts() != null) {
1127			printer.indent();
1128			for (final Statement s : n.getStmts()) {
1129				s.accept(this, arg);
1130				printer.printLn();
1131			}
1132			printer.unindent();
1133		}
1134		printOrphanCommentsEnding(n);
1135		printer.print("}");
1136
1137	}
1138
1139	@Override public void visit(final LabeledStmt n, final Object arg) {
1140		printJavaComment(n.getComment(), arg);
1141		printer.print(n.getLabel());
1142		printer.print(": ");
1143		n.getStmt().accept(this, arg);
1144	}
1145
1146	@Override public void visit(final EmptyStmt n, final Object arg) {
1147		printJavaComment(n.getComment(), arg);
1148		printer.print(";");
1149	}
1150
1151	@Override public void visit(final ExpressionStmt n, final Object arg) {
1152		printOrphanCommentsBeforeThisChildNode(n);
1153		printJavaComment(n.getComment(), arg);
1154		n.getExpression().accept(this, arg);
1155		printer.print(";");
1156	}
1157
1158	@Override public void visit(final SwitchStmt n, final Object arg) {
1159		printJavaComment(n.getComment(), arg);
1160		printer.print("switch(");
1161		n.getSelector().accept(this, arg);
1162		printer.printLn(") {");
1163		if (n.getEntries() != null) {
1164			printer.indent();
1165			for (final SwitchEntryStmt e : n.getEntries()) {
1166				e.accept(this, arg);
1167			}
1168			printer.unindent();
1169		}
1170		printer.print("}");
1171
1172	}
1173
1174	@Override public void visit(final SwitchEntryStmt n, final Object arg) {
1175		printJavaComment(n.getComment(), arg);
1176		if (n.getLabel() != null) {
1177			printer.print("case ");
1178			n.getLabel().accept(this, arg);
1179			printer.print(":");
1180		} else {
1181			printer.print("default:");
1182		}
1183		printer.printLn();
1184		printer.indent();
1185		if (n.getStmts() != null) {
1186			for (final Statement s : n.getStmts()) {
1187				s.accept(this, arg);
1188				printer.printLn();
1189			}
1190		}
1191		printer.unindent();
1192	}
1193
1194	@Override public void visit(final BreakStmt n, final Object arg) {
1195		printJavaComment(n.getComment(), arg);
1196		printer.print("break");
1197		if (n.getId() != null) {
1198			printer.print(" ");
1199			printer.print(n.getId());
1200		}
1201		printer.print(";");
1202	}
1203
1204	@Override public void visit(final ReturnStmt n, final Object arg) {
1205		printJavaComment(n.getComment(), arg);
1206		printer.print("return");
1207		if (n.getExpr() != null) {
1208			printer.print(" ");
1209			n.getExpr().accept(this, arg);
1210		}
1211		printer.print(";");
1212	}
1213
1214	@Override public void visit(final EnumDeclaration n, final Object arg) {
1215		printJavaComment(n.getComment(), arg);
1216		printJavadoc(n.getJavaDoc(), arg);
1217		printMemberAnnotations(n.getAnnotations(), arg);
1218		printModifiers(n.getModifiers());
1219
1220		printer.print("enum ");
1221		printer.print(n.getName());
1222
1223		if (n.getImplements() != null) {
1224			printer.print(" implements ");
1225			for (final Iterator<ClassOrInterfaceType> i = n.getImplements().iterator(); i.hasNext();) {
1226				final ClassOrInterfaceType c = i.next();
1227				c.accept(this, arg);
1228				if (i.hasNext()) {
1229					printer.print(", ");
1230				}
1231			}
1232		}
1233
1234		printer.printLn(" {");
1235		printer.indent();
1236		if (n.getEntries() != null) {
1237			printer.printLn();
1238			for (final Iterator<EnumConstantDeclaration> i = n.getEntries().iterator(); i.hasNext();) {
1239				final EnumConstantDeclaration e = i.next();
1240				e.accept(this, arg);
1241				if (i.hasNext()) {
1242					printer.print(", ");
1243				}
1244			}
1245		}
1246		if (n.getMembers() != null) {
1247			printer.printLn(";");
1248			printMembers(n.getMembers(), arg);
1249		} else {
1250			if (n.getEntries() != null) {
1251				printer.printLn();
1252			}
1253		}
1254		printer.unindent();
1255		printer.print("}");
1256	}
1257
1258	@Override public void visit(final EnumConstantDeclaration n, final Object arg) {
1259		printJavaComment(n.getComment(), arg);
1260		printJavadoc(n.getJavaDoc(), arg);
1261		printMemberAnnotations(n.getAnnotations(), arg);
1262		printer.print(n.getName());
1263
1264		if (n.getArgs() != null) {
1265			printArguments(n.getArgs(), arg);
1266		}
1267
1268		if (n.getClassBody() != null) {
1269			printer.printLn(" {");
1270			printer.indent();
1271			printMembers(n.getClassBody(), arg);
1272			printer.unindent();
1273			printer.printLn("}");
1274		}
1275	}
1276
1277	@Override public void visit(final EmptyMemberDeclaration n, final Object arg) {
1278		printJavaComment(n.getComment(), arg);
1279		printJavadoc(n.getJavaDoc(), arg);
1280		printer.print(";");
1281	}
1282
1283	@Override public void visit(final InitializerDeclaration n, final Object arg) {
1284		printJavaComment(n.getComment(), arg);
1285		printJavadoc(n.getJavaDoc(), arg);
1286		if (n.isStatic()) {
1287			printer.print("static ");
1288		}
1289		n.getBlock().accept(this, arg);
1290	}
1291
1292	@Override public void visit(final IfStmt n, final Object arg) {
1293		printJavaComment(n.getComment(), arg);
1294		printer.print("if (");
1295		n.getCondition().accept(this, arg);
1296		final boolean thenBlock = n.getThenStmt() instanceof BlockStmt;
1297		if (thenBlock) // block statement should start on the same line
1298			printer.print(") ");
1299		else {
1300			printer.printLn(")");
1301			printer.indent();
1302		}
1303		n.getThenStmt().accept(this, arg);
1304		if (!thenBlock)
1305			printer.unindent();
1306		if (n.getElseStmt() != null) {
1307			if (thenBlock)
1308				printer.print(" ");
1309			else
1310				printer.printLn();
1311			final boolean elseIf = n.getElseStmt() instanceof IfStmt;
1312			final boolean elseBlock = n.getElseStmt() instanceof BlockStmt;
1313			if (elseIf || elseBlock) // put chained if and start of block statement on a same level
1314				printer.print("else ");
1315			else {
1316				printer.printLn("else");
1317				printer.indent();
1318			}
1319			n.getElseStmt().accept(this, arg);
1320			if (!(elseIf || elseBlock))
1321				printer.unindent();
1322		}
1323	}
1324
1325	@Override public void visit(final WhileStmt n, final Object arg) {
1326		printJavaComment(n.getComment(), arg);
1327		printer.print("while (");
1328		n.getCondition().accept(this, arg);
1329		printer.print(") ");
1330		n.getBody().accept(this, arg);
1331	}
1332
1333	@Override public void visit(final ContinueStmt n, final Object arg) {
1334		printJavaComment(n.getComment(), arg);
1335		printer.print("continue");
1336		if (n.getId() != null) {
1337			printer.print(" ");
1338			printer.print(n.getId());
1339		}
1340		printer.print(";");
1341	}
1342
1343	@Override public void visit(final DoStmt n, final Object arg) {
1344		printJavaComment(n.getComment(), arg);
1345		printer.print("do ");
1346		n.getBody().accept(this, arg);
1347		printer.print(" while (");
1348		n.getCondition().accept(this, arg);
1349		printer.print(");");
1350	}
1351
1352	@Override public void visit(final ForeachStmt n, final Object arg) {
1353		printJavaComment(n.getComment(), arg);
1354		printer.print("for (");
1355		n.getVariable().accept(this, arg);
1356		printer.print(" : ");
1357		n.getIterable().accept(this, arg);
1358		printer.print(") ");
1359		n.getBody().accept(this, arg);
1360	}
1361
1362	@Override public void visit(final ForStmt n, final Object arg) {
1363		printJavaComment(n.getComment(), arg);
1364		printer.print("for (");
1365		if (n.getInit() != null) {
1366			for (final Iterator<Expression> i = n.getInit().iterator(); i.hasNext();) {
1367				final Expression e = i.next();
1368				e.accept(this, arg);
1369				if (i.hasNext()) {
1370					printer.print(", ");
1371				}
1372			}
1373		}
1374		printer.print("; ");
1375		if (n.getCompare() != null) {
1376			n.getCompare().accept(this, arg);
1377		}
1378		printer.print("; ");
1379		if (n.getUpdate() != null) {
1380			for (final Iterator<Expression> i = n.getUpdate().iterator(); i.hasNext();) {
1381				final Expression e = i.next();
1382				e.accept(this, arg);
1383				if (i.hasNext()) {
1384					printer.print(", ");
1385				}
1386			}
1387		}
1388		printer.print(") ");
1389		n.getBody().accept(this, arg);
1390	}
1391
1392	@Override public void visit(final ThrowStmt n, final Object arg) {
1393		printJavaComment(n.getComment(), arg);
1394		printer.print("throw ");
1395		n.getExpr().accept(this, arg);
1396		printer.print(";");
1397	}
1398
1399	@Override public void visit(final SynchronizedStmt n, final Object arg) {
1400		printJavaComment(n.getComment(), arg);
1401		printer.print("synchronized (");
1402		n.getExpr().accept(this, arg);
1403		printer.print(") ");
1404		n.getBlock().accept(this, arg);
1405	}
1406
1407	@Override public void visit(final TryStmt n, final Object arg) {
1408		printJavaComment(n.getComment(), arg);
1409		printer.print("try ");
1410		if (!n.getResources().isEmpty()) {
1411			printer.print("(");
1412			Iterator<VariableDeclarationExpr> resources = n.getResources().iterator();
1413			boolean first = true;
1414			while (resources.hasNext()) {
1415				visit(resources.next(), arg);
1416				if (resources.hasNext()) {
1417					printer.print(";");
1418					printer.printLn();
1419					if (first) {
1420						printer.indent();
1421					}
1422				}
1423				first = false;
1424			}
1425			if (n.getResources().size() > 1) {
1426				printer.unindent();
1427			}
1428			printer.print(") ");
1429		}
1430		n.getTryBlock().accept(this, arg);
1431		if (n.getCatchs() != null) {
1432			for (final CatchClause c : n.getCatchs()) {
1433				c.accept(this, arg);
1434			}
1435		}
1436		if (n.getFinallyBlock() != null) {
1437			printer.print(" finally ");
1438			n.getFinallyBlock().accept(this, arg);
1439		}
1440	}
1441
1442	@Override public void visit(final CatchClause n, final Object arg) {
1443		printJavaComment(n.getComment(), arg);
1444		printer.print(" catch (");
1445		n.getExcept().accept(this, arg);
1446		printer.print(") ");
1447		n.getCatchBlock().accept(this, arg);
1448
1449	}
1450
1451	@Override public void visit(final AnnotationDeclaration n, final Object arg) {
1452		printJavaComment(n.getComment(), arg);
1453		printJavadoc(n.getJavaDoc(), arg);
1454		printMemberAnnotations(n.getAnnotations(), arg);
1455		printModifiers(n.getModifiers());
1456
1457		printer.print("@interface ");
1458		printer.print(n.getName());
1459		printer.printLn(" {");
1460		printer.indent();
1461		if (n.getMembers() != null) {
1462			printMembers(n.getMembers(), arg);
1463		}
1464		printer.unindent();
1465		printer.print("}");
1466	}
1467
1468	@Override public void visit(final AnnotationMemberDeclaration n, final Object arg) {
1469		printJavaComment(n.getComment(), arg);
1470		printJavadoc(n.getJavaDoc(), arg);
1471		printMemberAnnotations(n.getAnnotations(), arg);
1472		printModifiers(n.getModifiers());
1473
1474		n.getType().accept(this, arg);
1475		printer.print(" ");
1476		printer.print(n.getName());
1477		printer.print("()");
1478		if (n.getDefaultValue() != null) {
1479			printer.print(" default ");
1480			n.getDefaultValue().accept(this, arg);
1481		}
1482		printer.print(";");
1483	}
1484
1485	@Override public void visit(final MarkerAnnotationExpr n, final Object arg) {
1486		printJavaComment(n.getComment(), arg);
1487		printer.print("@");
1488		n.getName().accept(this, arg);
1489	}
1490
1491	@Override public void visit(final SingleMemberAnnotationExpr n, final Object arg) {
1492		printJavaComment(n.getComment(), arg);
1493		printer.print("@");
1494		n.getName().accept(this, arg);
1495		printer.print("(");
1496		n.getMemberValue().accept(this, arg);
1497		printer.print(")");
1498	}
1499
1500	@Override public void visit(final NormalAnnotationExpr n, final Object arg) {
1501		printJavaComment(n.getComment(), arg);
1502		printer.print("@");
1503		n.getName().accept(this, arg);
1504		printer.print("(");
1505		if (n.getPairs() != null) {
1506			for (final Iterator<MemberValuePair> i = n.getPairs().iterator(); i.hasNext();) {
1507				final MemberValuePair m = i.next();
1508				m.accept(this, arg);
1509				if (i.hasNext()) {
1510					printer.print(", ");
1511				}
1512			}
1513		}
1514		printer.print(")");
1515	}
1516
1517	@Override public void visit(final MemberValuePair n, final Object arg) {
1518		printJavaComment(n.getComment(), arg);
1519		printer.print(n.getName());
1520		printer.print(" = ");
1521		n.getValue().accept(this, arg);
1522	}
1523
1524	@Override public void visit(final LineComment n, final Object arg) {
1525		if (!this.printComments) {
1526            return;
1527        }
1528        printer.print("//");
1529		String tmp = n.getContent();
1530		tmp = tmp.replace('\r', ' ');
1531		tmp = tmp.replace('\n', ' ');
1532		printer.printLn(tmp);
1533	}
1534
1535	@Override public void visit(final BlockComment n, final Object arg) {
1536        if (!this.printComments) {
1537            return;
1538        }
1539        printer.print("/*");
1540		printer.print(n.getContent());
1541		printer.printLn("*/");
1542	}
1543
1544	@Override
1545	public void visit(LambdaExpr n, Object arg) {
1546        printJavaComment(n.getComment(), arg);
1547
1548        List<Parameter> parameters = n.getParameters();
1549		boolean printPar = false;
1550		printPar = n.isParametersEnclosed();
1551
1552		if (printPar) {
1553			printer.print("(");
1554		}
1555		if (parameters != null) {
1556			for (Iterator<Parameter> i = parameters.iterator(); i.hasNext();) {
1557				Parameter p = i.next();
1558				p.accept(this, arg);
1559				if (i.hasNext()) {
1560					printer.print(", ");
1561				}
1562			}
1563		}
1564		if (printPar) {
1565			printer.print(")");
1566		}
1567
1568		printer.print(" -> ");
1569		Statement body = n.getBody();
1570		if (body instanceof ExpressionStmt) {
1571			// Print the expression directly
1572			((ExpressionStmt) body).getExpression().accept(this, arg);
1573		} else {
1574			body.accept(this, arg);
1575		}
1576	}
1577
1578
1579    @Override
1580    public void visit(MethodReferenceExpr n, Object arg) {
1581        printJavaComment(n.getComment(), arg);
1582        Expression scope = n.getScope();
1583        String identifier = n.getIdentifier();
1584        if (scope != null) {
1585            n.getScope().accept(this, arg);
1586        }
1587
1588        printer.print("::");
1589        if (n.getTypeParameters() != null) {
1590            printer.print("<");
1591            for (Iterator<TypeParameter> i = n.getTypeParameters().iterator(); i
1592                    .hasNext();) {
1593                TypeParameter p = i.next();
1594                p.accept(this, arg);
1595                if (i.hasNext()) {
1596                    printer.print(", ");
1597                }
1598            }
1599            printer.print(">");
1600        }
1601        if (identifier != null) {
1602            printer.print(identifier);
1603        }
1604
1605    }
1606
1607    @Override
1608    public void visit(TypeExpr n, Object arg) {
1609        printJavaComment(n.getComment(), arg);
1610        if (n.getType() != null) {
1611            n.getType().accept(this, arg);
1612        }
1613    }
1614
1615    private void printOrphanCommentsBeforeThisChildNode(final Node node){
1616        if (node instanceof Comment) return;
1617
1618        Node parent = node.getParentNode();
1619        if (parent==null) return;
1620        List<Node> everything = new LinkedList<Node>();
1621        everything.addAll(parent.getChildrenNodes());
1622        sortByBeginPosition(everything);
1623        int positionOfTheChild = -1;
1624        for (int i=0;i<everything.size();i++){
1625            if (everything.get(i)==node) positionOfTheChild=i;
1626        }
1627        if (positionOfTheChild==-1) throw new RuntimeException("My index not found!!! "+node);
1628        int positionOfPreviousChild = -1;
1629        for (int i=positionOfTheChild-1;i>=0 && positionOfPreviousChild==-1;i--){
1630            if (!(everything.get(i) instanceof Comment)) positionOfPreviousChild = i;
1631        }
1632        for (int i=positionOfPreviousChild+1;i<positionOfTheChild;i++){
1633            Node nodeToPrint = everything.get(i);
1634            if (!(nodeToPrint instanceof Comment)) throw new RuntimeException("Expected comment, instead "+nodeToPrint.getClass()+". Position of previous child: "+positionOfPreviousChild+", position of child "+positionOfTheChild);
1635            nodeToPrint.accept(this,null);
1636        }
1637    }
1638
1639
1640    private void printOrphanCommentsEnding(final Node node){
1641        List<Node> everything = new LinkedList<Node>();
1642        everything.addAll(node.getChildrenNodes());
1643        sortByBeginPosition(everything);
1644        if (everything.size()==0) return;
1645
1646        int commentsAtEnd = 0;
1647        boolean findingComments = true;
1648        while (findingComments&&commentsAtEnd<everything.size()){
1649            Node last = everything.get(everything.size()-1-commentsAtEnd);
1650            findingComments = (last instanceof Comment);
1651            if (findingComments) commentsAtEnd++;
1652        }
1653        for (int i=0;i<commentsAtEnd;i++){
1654            everything.get(everything.size()-commentsAtEnd+i).accept(this,null);
1655        }
1656    }
1657}
1658