All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.javaparser.printer.SourcePrinter Maven / Gradle / Ivy

/*
 * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
 * Copyright (C) 2011, 2013-2019 The JavaParser Team.
 *
 * This file is part of JavaParser.
 *
 * JavaParser can be used either under the terms of
 * a) the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * b) the terms of the Apache License
 *
 * You should have received a copy of both licenses in LICENCE.LGPL and
 * LICENCE.APACHE. Please refer to those files for details.
 *
 * JavaParser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 */

package com.github.javaparser.printer;

import com.github.javaparser.Position;
import com.github.javaparser.printer.PrettyPrinterConfiguration.IndentType;
import com.github.javaparser.utils.Utils;

import java.util.Deque;
import java.util.LinkedList;

import static com.github.javaparser.Position.*;

/**
 * A support class for code that outputs formatted source code.
 */
public class SourcePrinter {
    private final String endOfLineCharacter;
    private final String indentation;
    private final int tabWidth;
    private final IndentType indentType;

    private final Deque indents = new LinkedList<>();
    private final Deque reindentedIndents = new LinkedList<>();
    private String lastPrintedIndent = "";
    private final StringBuilder buf = new StringBuilder();
    private Position cursor = new Position(1, 0);
    private boolean indented = false;

    SourcePrinter() {
        this(new PrettyPrinterConfiguration());
    }

    SourcePrinter(final PrettyPrinterConfiguration configuration) {
        indentation = configuration.getIndent();
        endOfLineCharacter = configuration.getEndOfLineCharacter();
        tabWidth = configuration.getTabWidth();
        indentType = configuration.getIndentType();
        indents.push("");
    }

    /**
     * Add the default indentation to the current indentation and push it on the indentation stack.
     * Does not actually output anything.
     */
    public SourcePrinter indent() {
        String currentIndent = indents.peek();
        switch (indentType) {
            case SPACES:
            case TABS_WITH_SPACE_ALIGN:
                indents.push(currentIndent + indentation);
                break;

            case TABS:
                indents.push(indentation + currentIndent);
                break;

            default:
                throw new AssertionError("Unhandled indent type");
        }
        return this;
    }

    /**
     * Add to the current indentation until it is reaches "column" and push it on the indentation stack.
     * Does not actually output anything.
     */
    public SourcePrinter indentWithAlignTo(int column) {
        indents.push(calculateIndentWithAlignTo(column));
        return this;
    }

    private String calculateIndentWithAlignTo(int column) {
        if (column < lastPrintedIndent.length()){
            throw new IllegalStateException("Attempt to indent less than the previous indent.");
        }

        StringBuilder newIndent = new StringBuilder(lastPrintedIndent);
        switch (indentType) {
            case SPACES:
            case TABS_WITH_SPACE_ALIGN:
                while (newIndent.length() < column) {
                    newIndent.append(' ');
                }
                break;

            case TABS:
                int logicalIndentLength = newIndent.length();
                while ((logicalIndentLength + tabWidth) <= column) {
                    newIndent.insert(0, '\t');
                    logicalIndentLength += tabWidth;
                }
                while (logicalIndentLength < column) {
                    newIndent.append(' ');
                    logicalIndentLength++;
                }
                StringBuilder fullTab = new StringBuilder();
                for(int i=0; i= tabWidth)
                        && newIndent.substring(newIndent.length() - tabWidth).equals(fullTabString)) {
                    int i = newIndent.indexOf(fullTabString);
                    newIndent.replace(i, i + tabWidth, "\t");
                }
                break;

            default:
                throw new AssertionError("Unhandled indent type");
        }

        return newIndent.toString();
    }

    /**
     * Pop the last indentation of the indentation stack.
     * Does not actually output anything.
     */
    public SourcePrinter unindent() {
        if (indents.isEmpty()) {
            // Since we start out with an empty indent on the stack, this will only occur
            // the second time we over-unindent.
            throw new IllegalStateException("Indent/unindent calls are not well-balanced.");
        }
        indents.pop();
        return this;
    }

    private void append(String arg) {
        buf.append(arg);
        cursor = cursor.withColumn(cursor.column + arg.length());
    }

    /**
     * Append the source string passed as argument to the buffer.
     * If this is being appended at the beginning of a line, performs indentation first.
     * 

* The source line to be printed should not contain newline/carriage-return characters; * use {@link #println(String)} to automatically append a newline at the end of the source string. * If the source line passed as argument contains newline/carriage-return characters would * impredictably affect a correct computation of the current {@link #getCursor()} position. * * @param arg source line to be printed (should not contain newline/carriage-return characters) * @return this instance, for nesting calls to method as fluent interface * @see SourcePrinter#println(String) */ public SourcePrinter print(final String arg) { if (!indented) { lastPrintedIndent = indents.peek(); append(lastPrintedIndent); indented = true; } append(arg); return this; } /** * Append the source string passed as argument to the buffer, then append a newline. * If this is being appended at the beginning of a line, performs indentation first. *

* The source line to be printed should not contain newline/carriage-return characters. * If the source line passed as argument contains newline/carriage-return characters would * impredictably affect a correct computation of the current {@link #getCursor()} position. * * @param arg source line to be printed (should not contain newline/carriage-return characters) * @return this instance, for nesting calls to method as fluent interface */ public SourcePrinter println(final String arg) { print(arg); println(); return this; } /** * Append a newline to the buffer. * * @return this instance, for nesting calls to method as fluent interface */ public SourcePrinter println() { buf.append(endOfLineCharacter); cursor = pos(cursor.line + 1, 0); indented = false; return this; } /** * Return the current cursor position (line, column) in the source printer buffer. *

* Please notice in order to guarantee a correct computation of the cursor position, * this printer expect the contracts of the methods {@link #print(String)} and {@link #println(String)} * has been respected through all method calls, meaning the source string passed as argument to those method * calls did not contain newline/carriage-return characters. * * @return the current cursor position (line, column). */ public Position getCursor() { return cursor; } /** * @return the currently printed source code. * @deprecated use toString() */ @Deprecated public String getSource() { return toString(); } /** * @return the currently printed source code. */ @Override public String toString() { return buf.toString(); } /** * Changes all EOL characters in "content" to the EOL character this SourcePrinter is using. */ public String normalizeEolInTextBlock(String content) { return Utils.normalizeEolInTextBlock(content, endOfLineCharacter); } /** * Set the top-most indent to the column the cursor is currently in, can be undone with * {@link #reindentToPreviousLevel()}. Does not actually output anything. */ public void reindentWithAlignToCursor() { String newIndent = calculateIndentWithAlignTo(cursor.column); reindentedIndents.push(indents.pop()); indents.push(newIndent); } /** * Set the top-most indent to the column the cursor was before the last {@link #reindentWithAlignToCursor()} call. * Does not actually output anything. */ public void reindentToPreviousLevel() { if (reindentedIndents.isEmpty()) { throw new IllegalStateException("Reindent calls are not well-balanced."); } indents.pop(); indents.push(reindentedIndents.pop()); } /** * Adds an indent to the top of the stack that is a copy of the current top indent. * With this you announce "I'm going to indent the next line(s)" but not how far yet. * Once you do know, you can pop this indent ("unindent") and indent to the right column. * (Does not actually output anything.) */ public void duplicateIndent() { indents.push(indents.peek()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy