com.google.dart.java2dart.util.ToFormattedSourceVisitor.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.java2dart.util.ToFormattedSourceVisitor.java

Source

/*
 * Copyright (c) 2012, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.dart.java2dart.util;

import com.google.dart.engine.ast.*;
import com.google.dart.engine.scanner.Token;
import com.google.dart.engine.utilities.dart.ParameterKind;

import org.apache.commons.lang3.StringUtils;

import java.io.PrintWriter;

/**
 * Instances of the class {link ToFormattedSourceVisitor} write a source representation of a visited
 * AST node (and all of it's children) to a writer.
 */
public class ToFormattedSourceVisitor implements ASTVisitor<Void> {
    /**
     * The writer to which the source is to be written.
     */
    private PrintWriter writer;
    private int indentLevel = 0;
    private String indentString = "";

    /**
     * Initialize a newly created visitor to write source code representing the visited nodes to the
     * given writer.
     * 
     * @param writer the writer to which the source is to be written
     */
    public ToFormattedSourceVisitor(PrintWriter writer) {
        this.writer = writer;
    }

    @Override
    public Void visitAdjacentStrings(AdjacentStrings node) {
        visitList(node.getStrings(), " ");
        return null;
    }

    @Override
    public Void visitAnnotation(Annotation node) {
        writer.print('@');
        visit(node.getName());
        visit(".", node.getConstructorName());
        visit(node.getArguments());
        return null;
    }

    @Override
    public Void visitArgumentDefinitionTest(ArgumentDefinitionTest node) {
        writer.print('?');
        visit(node.getIdentifier());
        return null;
    }

    @Override
    public Void visitArgumentList(ArgumentList node) {
        writer.print('(');
        visitList(node.getArguments(), ", ");
        writer.print(')');
        return null;
    }

    @Override
    public Void visitAsExpression(AsExpression node) {
        visit(node.getExpression());
        writer.print(" as ");
        visit(node.getType());
        return null;
    }

    @Override
    public Void visitAssertStatement(AssertStatement node) {
        writer.print("assert(");
        visit(node.getCondition());
        writer.print(");");
        return null;
    }

    @Override
    public Void visitAssignmentExpression(AssignmentExpression node) {
        visit(node.getLeftHandSide());
        writer.print(' ');
        writer.print(node.getOperator().getLexeme());
        writer.print(' ');
        visit(node.getRightHandSide());
        return null;
    }

    @Override
    public Void visitBinaryExpression(BinaryExpression node) {
        visit(node.getLeftOperand());
        writer.print(' ');
        writer.print(node.getOperator().getLexeme());
        writer.print(' ');
        visit(node.getRightOperand());
        return null;
    }

    @Override
    public Void visitBlock(Block node) {
        writer.print('{');
        {
            indentInc();
            visitList(node.getStatements(), "\n");
            indentDec();
        }
        nl2();
        writer.print('}');
        return null;
    }

    @Override
    public Void visitBlockFunctionBody(BlockFunctionBody node) {
        visit(node.getBlock());
        return null;
    }

    @Override
    public Void visitBooleanLiteral(BooleanLiteral node) {
        writer.print(node.getLiteral().getLexeme());
        return null;
    }

    @Override
    public Void visitBreakStatement(BreakStatement node) {
        writer.print("break");
        visit(" ", node.getLabel());
        writer.print(";");
        return null;
    }

    @Override
    public Void visitCascadeExpression(CascadeExpression node) {
        visit(node.getTarget());
        visitList(node.getCascadeSections());
        return null;
    }

    @Override
    public Void visitCatchClause(CatchClause node) {
        visit("on ", node.getExceptionType());
        if (node.getCatchKeyword() != null) {
            if (node.getExceptionType() != null) {
                writer.print(' ');
            }
            writer.print("catch (");
            visit(node.getExceptionParameter());
            visit(", ", node.getStackTraceParameter());
            writer.print(") ");
        } else {
            writer.print(" ");
        }
        visit(node.getBody());
        return null;
    }

    @Override
    public Void visitClassDeclaration(ClassDeclaration node) {
        visit(node.getDocumentationComment());
        visit(node.getAbstractKeyword(), " ");
        writer.print("class ");
        visit(node.getName());
        visit(node.getTypeParameters());
        visit(" ", node.getExtendsClause());
        visit(" ", node.getWithClause());
        visit(" ", node.getImplementsClause());
        writer.print(" {");
        {
            indentInc();
            visitList(node.getMembers(), "\n");
            indentDec();
        }
        nl2();
        writer.print("}");
        return null;
    }

    @Override
    public Void visitClassTypeAlias(ClassTypeAlias node) {
        writer.print("typedef ");
        visit(node.getName());
        visit(node.getTypeParameters());
        writer.print(" = ");
        if (node.getAbstractKeyword() != null) {
            writer.print("abstract ");
        }
        visit(node.getSuperclass());
        visit(" ", node.getWithClause());
        visit(" ", node.getImplementsClause());
        writer.print(";");
        return null;
    }

    @Override
    public Void visitComment(Comment node) {
        Token token = node.getBeginToken();
        while (token != null) {
            boolean firstLine = true;
            for (String line : StringUtils.split(token.getLexeme(), "\n")) {
                if (firstLine) {
                    firstLine = false;
                    // TODO (danrubel): clean this up if isDocumentation() is modified to include ///
                    if (node.isDocumentation()) {
                        nl2();
                    }
                } else {
                    line = " " + line.trim();
                    line = StringUtils.replace(line, "/*", "/ *");
                }
                writer.print(line);
                nl2();
            }
            if (token == node.getEndToken()) {
                break;
            }
        }
        return null;
    }

    @Override
    public Void visitCommentReference(CommentReference node) {
        // We don't print comment references.
        return null;
    }

    @Override
    public Void visitCompilationUnit(CompilationUnit node) {
        ScriptTag scriptTag = node.getScriptTag();
        NodeList<Directive> directives = node.getDirectives();
        visit(scriptTag);
        String prefix = scriptTag == null ? "" : " ";
        visitList(prefix, directives, "\n");
        prefix = scriptTag == null && directives.isEmpty() ? "" : "\n\n";
        visitList(prefix, node.getDeclarations(), "\n");
        return null;
    }

    @Override
    public Void visitConditionalExpression(ConditionalExpression node) {
        visit(node.getCondition());
        writer.print(" ? ");
        visit(node.getThenExpression());
        writer.print(" : ");
        visit(node.getElseExpression());
        return null;
    }

    @Override
    public Void visitConstructorDeclaration(ConstructorDeclaration node) {
        visit(node.getDocumentationComment());
        visit(node.getExternalKeyword(), " ");
        visit(node.getConstKeyword(), " ");
        visit(node.getFactoryKeyword(), " ");
        visit(node.getReturnType());
        visit(".", node.getName());
        visit(node.getParameters());
        visitList(" : ", node.getInitializers(), ", ");
        visit(" = ", node.getRedirectedConstructor());
        if (!(node.getBody() instanceof EmptyFunctionBody)) {
            writer.print(' ');
        }
        visit(node.getBody());
        return null;
    }

    @Override
    public Void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
        visit(node.getKeyword(), ".");
        visit(node.getFieldName());
        writer.print(" = ");
        visit(node.getExpression());
        return null;
    }

    @Override
    public Void visitConstructorName(ConstructorName node) {
        visit(node.getType());
        visit(".", node.getName());
        return null;
    }

    @Override
    public Void visitContinueStatement(ContinueStatement node) {
        writer.print("continue");
        visit(" ", node.getLabel());
        writer.print(";");
        return null;
    }

    @Override
    public Void visitDeclaredIdentifier(DeclaredIdentifier node) {
        visit(node.getKeyword(), " ");
        visit(node.getType(), " ");
        visit(node.getIdentifier());
        return null;
    }

    @Override
    public Void visitDefaultFormalParameter(DefaultFormalParameter node) {
        visit(node.getParameter());
        if (node.getSeparator() != null) {
            writer.print(" ");
            writer.print(node.getSeparator().getLexeme());
            visit(" ", node.getDefaultValue());
        }
        return null;
    }

    @Override
    public Void visitDoStatement(DoStatement node) {
        writer.print("do ");
        visit(node.getBody());
        writer.print(" while (");
        visit(node.getCondition());
        writer.print(");");
        return null;
    }

    @Override
    public Void visitDoubleLiteral(DoubleLiteral node) {
        writer.print(node.getLiteral().getLexeme());
        return null;
    }

    @Override
    public Void visitEmptyFunctionBody(EmptyFunctionBody node) {
        writer.print(';');
        return null;
    }

    @Override
    public Void visitEmptyStatement(EmptyStatement node) {
        writer.print(';');
        return null;
    }

    @Override
    public Void visitExportDirective(ExportDirective node) {
        writer.print("export ");
        visit(node.getUri());
        visitList(" ", node.getCombinators(), " ");
        writer.print(';');
        return null;
    }

    @Override
    public Void visitExpressionFunctionBody(ExpressionFunctionBody node) {
        writer.print("=> ");
        visit(node.getExpression());
        if (node.getSemicolon() != null) {
            writer.print(';');
        }
        return null;
    }

    @Override
    public Void visitExpressionStatement(ExpressionStatement node) {
        visit(node.getExpression());
        writer.print(';');
        return null;
    }

    @Override
    public Void visitExtendsClause(ExtendsClause node) {
        writer.print("extends ");
        visit(node.getSuperclass());
        return null;
    }

    @Override
    public Void visitFieldDeclaration(FieldDeclaration node) {
        visit(node.getDocumentationComment());
        visit(node.getKeyword(), " ");
        visit(node.getFields());
        writer.print(";");
        return null;
    }

    @Override
    public Void visitFieldFormalParameter(FieldFormalParameter node) {
        visit(node.getKeyword(), " ");
        visit(node.getType(), " ");
        writer.print("this.");
        visit(node.getIdentifier());
        return null;
    }

    @Override
    public Void visitForEachStatement(ForEachStatement node) {
        writer.print("for (");
        visit(node.getLoopVariable());
        writer.print(" in ");
        visit(node.getIterator());
        writer.print(") ");
        visit(node.getBody());
        return null;
    }

    @Override
    public Void visitFormalParameterList(FormalParameterList node) {
        String groupEnd = null;
        writer.print('(');
        NodeList<FormalParameter> parameters = node.getParameters();
        int size = parameters.size();
        for (int i = 0; i < size; i++) {
            FormalParameter parameter = parameters.get(i);
            if (i > 0) {
                writer.print(", ");
            }
            if (groupEnd == null && parameter instanceof DefaultFormalParameter) {
                if (parameter.getKind() == ParameterKind.NAMED) {
                    groupEnd = "}";
                    writer.print('{');
                } else {
                    groupEnd = "]";
                    writer.print('[');
                }
            }
            parameter.accept(this);
        }
        if (groupEnd != null) {
            writer.print(groupEnd);
        }
        writer.print(')');
        return null;
    }

    @Override
    public Void visitForStatement(ForStatement node) {
        Expression initialization = node.getInitialization();
        writer.print("for (");
        if (initialization != null) {
            visit(initialization);
        } else {
            visit(node.getVariables());
        }
        writer.print(";");
        visit(" ", node.getCondition());
        writer.print(";");
        visitList(" ", node.getUpdaters(), ", ");
        writer.print(") ");
        visit(node.getBody());
        return null;
    }

    @Override
    public Void visitFunctionDeclaration(FunctionDeclaration node) {
        visit(node.getReturnType(), " ");
        visit(node.getPropertyKeyword(), " ");
        visit(node.getName());
        visit(node.getFunctionExpression());
        return null;
    }

    @Override
    public Void visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
        visit(node.getFunctionDeclaration());
        writer.print(';');
        return null;
    }

    @Override
    public Void visitFunctionExpression(FunctionExpression node) {
        visit(node.getParameters());
        writer.print(' ');
        visit(node.getBody());
        return null;
    }

    @Override
    public Void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
        visit(node.getFunction());
        visit(node.getArgumentList());
        return null;
    }

    @Override
    public Void visitFunctionTypeAlias(FunctionTypeAlias node) {
        writer.print("typedef ");
        visit(node.getReturnType(), " ");
        visit(node.getName());
        visit(node.getTypeParameters());
        visit(node.getParameters());
        writer.print(";");
        return null;
    }

    @Override
    public Void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
        visit(node.getReturnType(), " ");
        visit(node.getIdentifier());
        visit(node.getParameters());
        return null;
    }

    @Override
    public Void visitHideCombinator(HideCombinator node) {
        writer.print("hide ");
        visitList(node.getHiddenNames(), ", ");
        return null;
    }

    @Override
    public Void visitIfStatement(IfStatement node) {
        writer.print("if (");
        visit(node.getCondition());
        writer.print(") ");
        visit(node.getThenStatement());
        visit(" else ", node.getElseStatement());
        return null;
    }

    @Override
    public Void visitImplementsClause(ImplementsClause node) {
        writer.print("implements ");
        visitList(node.getInterfaces(), ", ");
        return null;
    }

    @Override
    public Void visitImportDirective(ImportDirective node) {
        writer.print("import ");
        visit(node.getUri());
        visit(" as ", node.getPrefix());
        visitList(" ", node.getCombinators(), " ");
        writer.print(';');
        return null;
    }

    @Override
    public Void visitIndexExpression(IndexExpression node) {
        if (node.isCascaded()) {
            writer.print("..");
        } else {
            visit(node.getArray());
        }
        writer.print('[');
        visit(node.getIndex());
        writer.print(']');
        return null;
    }

    @Override
    public Void visitInstanceCreationExpression(InstanceCreationExpression node) {
        visit(node.getKeyword(), " ");
        visit(node.getConstructorName());
        visit(node.getArgumentList());
        return null;
    }

    @Override
    public Void visitIntegerLiteral(IntegerLiteral node) {
        writer.print(node.getLiteral().getLexeme());
        return null;
    }

    @Override
    public Void visitInterpolationExpression(InterpolationExpression node) {
        if (node.getRightBracket() != null) {
            writer.print("${");
            visit(node.getExpression());
            writer.print("}");
        } else {
            writer.print("$");
            visit(node.getExpression());
        }
        return null;
    }

    @Override
    public Void visitInterpolationString(InterpolationString node) {
        writer.print(node.getContents().getLexeme());
        return null;
    }

    @Override
    public Void visitIsExpression(IsExpression node) {
        visit(node.getExpression());
        if (node.getNotOperator() == null) {
            writer.print(" is ");
        } else {
            writer.print(" is! ");
        }
        visit(node.getType());
        return null;
    }

    @Override
    public Void visitLabel(Label node) {
        visit(node.getLabel());
        writer.print(":");
        return null;
    }

    @Override
    public Void visitLabeledStatement(LabeledStatement node) {
        visitList(node.getLabels(), " ", " ");
        visit(node.getStatement());
        return null;
    }

    @Override
    public Void visitLibraryDirective(LibraryDirective node) {
        writer.print("library ");
        visit(node.getName());
        writer.print(';');
        nl();
        return null;
    }

    @Override
    public Void visitLibraryIdentifier(LibraryIdentifier node) {
        writer.print(node.getName());
        return null;
    }

    @Override
    public Void visitListLiteral(ListLiteral node) {
        if (node.getModifier() != null) {
            writer.print(node.getModifier().getLexeme());
            writer.print(' ');
        }
        visit(node.getTypeArguments(), " ");
        writer.print("[");
        visitList(node.getElements(), ", ");
        writer.print("]");
        return null;
    }

    @Override
    public Void visitMapLiteral(MapLiteral node) {
        if (node.getModifier() != null) {
            writer.print(node.getModifier().getLexeme());
            writer.print(' ');
        }
        visit(node.getTypeArguments(), " ");
        writer.print("{");
        visitList(node.getEntries(), ", ");
        writer.print("}");
        return null;
    }

    @Override
    public Void visitMapLiteralEntry(MapLiteralEntry node) {
        visit(node.getKey());
        writer.print(" : ");
        visit(node.getValue());
        return null;
    }

    @Override
    public Void visitMethodDeclaration(MethodDeclaration node) {
        visit(node.getDocumentationComment());
        visit(node.getExternalKeyword(), " ");
        visit(node.getModifierKeyword(), " ");
        visit(node.getReturnType(), " ");
        visit(node.getPropertyKeyword(), " ");
        visit(node.getOperatorKeyword(), " ");
        visit(node.getName());
        if (!node.isGetter()) {
            visit(node.getParameters());
        }
        if (!(node.getBody() instanceof EmptyFunctionBody)) {
            writer.print(' ');
        }
        visit(node.getBody());
        return null;
    }

    @Override
    public Void visitMethodInvocation(MethodInvocation node) {
        if (node.isCascaded()) {
            writer.print("..");
        } else {
            visit(node.getTarget(), ".");
        }
        visit(node.getMethodName());
        visit(node.getArgumentList());
        return null;
    }

    @Override
    public Void visitNamedExpression(NamedExpression node) {
        visit(node.getName());
        visit(" ", node.getExpression());
        return null;
    }

    @Override
    public Void visitNativeFunctionBody(NativeFunctionBody node) {
        writer.print("native ");
        visit(node.getStringLiteral());
        writer.print(';');
        return null;
    }

    @Override
    public Void visitNullLiteral(NullLiteral node) {
        writer.print("null");
        return null;
    }

    @Override
    public Void visitParenthesizedExpression(ParenthesizedExpression node) {
        writer.print('(');
        visit(node.getExpression());
        writer.print(')');
        return null;
    }

    @Override
    public Void visitPartDirective(PartDirective node) {
        writer.print("part ");
        visit(node.getUri());
        writer.print(';');
        return null;
    }

    @Override
    public Void visitPartOfDirective(PartOfDirective node) {
        writer.print("part of ");
        visit(node.getLibraryName());
        writer.print(';');
        return null;
    }

    @Override
    public Void visitPostfixExpression(PostfixExpression node) {
        visit(node.getOperand());
        writer.print(node.getOperator().getLexeme());
        return null;
    }

    @Override
    public Void visitPrefixedIdentifier(PrefixedIdentifier node) {
        visit(node.getPrefix());
        writer.print('.');
        visit(node.getIdentifier());
        return null;
    }

    @Override
    public Void visitPrefixExpression(PrefixExpression node) {
        writer.print(node.getOperator().getLexeme());
        visit(node.getOperand());
        return null;
    }

    @Override
    public Void visitPropertyAccess(PropertyAccess node) {
        if (node.isCascaded()) {
            writer.print("..");
        } else {
            visit(node.getTarget(), ".");
        }
        visit(node.getPropertyName());
        return null;
    }

    @Override
    public Void visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
        writer.print("this");
        visit(".", node.getConstructorName());
        visit(node.getArgumentList());
        return null;
    }

    @Override
    public Void visitRethrowExpression(RethrowExpression node) {
        writer.print("rethrow");
        return null;
    }

    @Override
    public Void visitReturnStatement(ReturnStatement node) {
        Expression expression = node.getExpression();
        if (expression == null) {
            writer.print("return;");
        } else {
            writer.print("return ");
            expression.accept(this);
            writer.print(";");
        }
        return null;
    }

    @Override
    public Void visitScriptTag(ScriptTag node) {
        writer.print(node.getScriptTag().getLexeme());
        return null;
    }

    @Override
    public Void visitShowCombinator(ShowCombinator node) {
        writer.print("show ");
        visitList(node.getShownNames(), ", ");
        return null;
    }

    @Override
    public Void visitSimpleFormalParameter(SimpleFormalParameter node) {
        visit(node.getKeyword(), " ");
        visit(node.getType(), " ");
        visit(node.getIdentifier());
        return null;
    }

    @Override
    public Void visitSimpleIdentifier(SimpleIdentifier node) {
        writer.print(node.getToken().getLexeme());
        return null;
    }

    @Override
    public Void visitSimpleStringLiteral(SimpleStringLiteral node) {
        writer.print(node.getLiteral().getLexeme());
        return null;
    }

    @Override
    public Void visitStringInterpolation(StringInterpolation node) {
        visitList(node.getElements());
        return null;
    }

    @Override
    public Void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
        writer.print("super");
        visit(".", node.getConstructorName());
        visit(node.getArgumentList());
        return null;
    }

    @Override
    public Void visitSuperExpression(SuperExpression node) {
        writer.print("super");
        return null;
    }

    @Override
    public Void visitSwitchCase(SwitchCase node) {
        visitList(node.getLabels(), " ", " ");
        writer.print("case ");
        visit(node.getExpression());
        writer.print(": ");
        {
            indentInc();
            visitList(node.getStatements(), "\n");
            indentDec();
        }
        return null;
    }

    @Override
    public Void visitSwitchDefault(SwitchDefault node) {
        visitList(node.getLabels(), " ", " ");
        writer.print("default: ");
        {
            indentInc();
            visitList(node.getStatements(), "\n");
            indentDec();
        }
        return null;
    }

    @Override
    public Void visitSwitchStatement(SwitchStatement node) {
        writer.print("switch (");
        visit(node.getExpression());
        writer.print(") {");
        {
            indentInc();
            visitList(node.getMembers(), "\n");
            indentDec();
        }
        nl2();
        writer.print('}');
        return null;
    }

    @Override
    public Void visitThisExpression(ThisExpression node) {
        writer.print("this");
        return null;
    }

    @Override
    public Void visitThrowExpression(ThrowExpression node) {
        writer.print("throw ");
        visit(node.getExpression());
        return null;
    }

    @Override
    public Void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
        visit(node.getVariables(), ";");
        return null;
    }

    @Override
    public Void visitTryStatement(TryStatement node) {
        writer.print("try ");
        visit(node.getBody());
        visitList(" ", node.getCatchClauses(), " ");
        visit(" finally ", node.getFinallyClause());
        return null;
    }

    @Override
    public Void visitTypeArgumentList(TypeArgumentList node) {
        writer.print('<');
        visitList(node.getArguments(), ", ");
        writer.print('>');
        return null;
    }

    @Override
    public Void visitTypeName(TypeName node) {
        visit(node.getName());
        visit(node.getTypeArguments());
        return null;
    }

    @Override
    public Void visitTypeParameter(TypeParameter node) {
        visit(node.getName());
        visit(" extends ", node.getBound());
        return null;
    }

    @Override
    public Void visitTypeParameterList(TypeParameterList node) {
        writer.print('<');
        visitList(node.getTypeParameters(), ", ");
        writer.print('>');
        return null;
    }

    @Override
    public Void visitVariableDeclaration(VariableDeclaration node) {
        visit(node.getName());
        visit(" = ", node.getInitializer());
        return null;
    }

    @Override
    public Void visitVariableDeclarationList(VariableDeclarationList node) {
        visit(node.getKeyword(), " ");
        visit(node.getType(), " ");
        visitList(node.getVariables(), ", ");
        return null;
    }

    @Override
    public Void visitVariableDeclarationStatement(VariableDeclarationStatement node) {
        visit(node.getVariables());
        writer.print(";");
        return null;
    }

    @Override
    public Void visitWhileStatement(WhileStatement node) {
        writer.print("while (");
        visit(node.getCondition());
        writer.print(") ");
        visit(node.getBody());
        return null;
    }

    @Override
    public Void visitWithClause(WithClause node) {
        writer.print("with ");
        visitList(node.getMixinTypes(), ", ");
        return null;
    }

    private void indent() {
        writer.print(indentString);
    }

    private void indentDec() {
        indentLevel -= 2;
        indentString = StringUtils.repeat(" ", indentLevel);
    }

    private void indentInc() {
        indentLevel += 2;
        indentString = StringUtils.repeat(" ", indentLevel);
    }

    private void nl() {
        writer.print("\n");
    }

    private void nl2() {
        nl();
        indent();
    }

    /**
     * Safely visit the given node.
     * 
     * @param node the node to be visited
     */
    private void visit(ASTNode node) {
        if (node != null) {
            node.accept(this);
        }
    }

    /**
     * Safely visit the given node, printing the suffix after the node if it is non-<code>null</code>.
     * 
     * @param suffix the suffix to be printed if there is a node to visit
     * @param node the node to be visited
     */
    private void visit(ASTNode node, String suffix) {
        if (node != null) {
            node.accept(this);
            writer.print(suffix);
        }
    }

    /**
     * Safely visit the given node, printing the prefix before the node if it is non-<code>null</code>
     * .
     * 
     * @param prefix the prefix to be printed if there is a node to visit
     * @param node the node to be visited
     */
    private void visit(String prefix, ASTNode node) {
        if (node != null) {
            writer.print(prefix);
            node.accept(this);
        }
    }

    /**
     * Safely visit the given node, printing the suffix after the node if it is non-<code>null</code>.
     * 
     * @param suffix the suffix to be printed if there is a node to visit
     * @param node the node to be visited
     */
    private void visit(Token token, String suffix) {
        if (token != null) {
            writer.print(token.getLexeme());
            writer.print(suffix);
        }
    }

    /**
     * Print a list of nodes without any separation.
     * 
     * @param nodes the nodes to be printed
     * @param separator the separator to be printed between adjacent nodes
     */
    private void visitList(NodeList<? extends ASTNode> nodes) {
        visitList(nodes, "");
    }

    /**
     * Print a list of nodes, separated by the given separator.
     * 
     * @param nodes the nodes to be printed
     * @param separator the separator to be printed between adjacent nodes
     */
    private void visitList(NodeList<? extends ASTNode> nodes, String separator) {
        if (nodes != null) {
            int size = nodes.size();
            for (int i = 0; i < size; i++) {
                if ("\n".equals(separator)) {
                    writer.print("\n");
                    indent();
                } else if (i > 0) {
                    writer.print(separator);
                }
                nodes.get(i).accept(this);
            }
        }
    }

    /**
     * Print a list of nodes, separated by the given separator.
     * 
     * @param nodes the nodes to be printed
     * @param separator the separator to be printed between adjacent nodes
     * @param suffix the suffix to be printed if the list is not empty
     */
    private void visitList(NodeList<? extends ASTNode> nodes, String separator, String suffix) {
        if (nodes != null) {
            int size = nodes.size();
            if (size > 0) {
                for (int i = 0; i < size; i++) {
                    if (i > 0) {
                        writer.print(separator);
                    }
                    nodes.get(i).accept(this);
                }
                writer.print(suffix);
            }
        }
    }

    /**
     * Print a list of nodes, separated by the given separator.
     * 
     * @param prefix the prefix to be printed if the list is not empty
     * @param nodes the nodes to be printed
     * @param separator the separator to be printed between adjacent nodes
     */
    private void visitList(String prefix, NodeList<? extends ASTNode> nodes, String separator) {
        if (nodes != null) {
            int size = nodes.size();
            if (size > 0) {
                writer.print(prefix);
                for (int i = 0; i < size; i++) {
                    if (i > 0) {
                        writer.print(separator);
                    }
                    nodes.get(i).accept(this);
                }
            }
        }
    }
}