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-2023 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.configuration.*;
import com.github.javaparser.printer.configuration.DefaultPrinterConfiguration.ConfigOption;
import com.github.javaparser.printer.configuration.Indentation.IndentType;
import com.github.javaparser.utils.Utils;

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

/**
 * A support class for code that outputs formatted source code.
 */
public class SourcePrinter {

    private String endOfLineCharacter;

    private Indentation indentation;

    private final Deque indents = new LinkedList<>();

    private final Deque reindentedIndents = new LinkedList<>();

    private String lastPrintedIndent = "";

    private final StringBuilder buf = new StringBuilder();

    // Start before the first column
    private Position cursor = new Position(Position.FIRST_LINE, Position.FIRST_COLUMN - 1);

    private boolean indented = false;

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

    SourcePrinter(final PrettyPrinterConfiguration configuration) {
        this(configuration.getIndentation(), configuration.getEndOfLineCharacter());
    }

    SourcePrinter(final PrinterConfiguration configuration) {
        this(configuration.get(new DefaultConfigurationOption(ConfigOption.INDENTATION)).get().asValue(), configuration.get(new DefaultConfigurationOption(ConfigOption.END_OF_LINE_CHARACTER)).get().asString());
    }

    SourcePrinter(Indentation indentation, String eol) {
        this.indentation = indentation;
        this.endOfLineCharacter = eol;
        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(indentation.getType()) {
            case SPACES:
            case TABS_WITH_SPACE_ALIGN:
                indents.push(currentIndent + indentation.getIndent());
                break;
            case TABS:
                indents.push(indentation.getIndent() + 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(indentation.getType()) {
            case SPACES:
            case TABS_WITH_SPACE_ALIGN:
                while (newIndent.length() < column) {
                    newIndent.append(IndentType.SPACES.getCar());
                }
                break;
            case TABS:
                IndentType currentIndentType = indentation.getType();
                int logicalIndentLength = newIndent.length();
                while ((logicalIndentLength + currentIndentType.getWidth()) <= column) {
                    newIndent.insert(0, currentIndentType.getCar());
                    logicalIndentLength += currentIndentType.getWidth();
                }
                while (logicalIndentLength < column) {
                    newIndent.append(IndentType.SPACES.getCar());
                    logicalIndentLength++;
                }
                StringBuilder fullTab = new StringBuilder();
                for (int i = 0; i < currentIndentType.getWidth(); i++) {
                    fullTab.append(IndentType.SPACES.getCar());
                }
                String fullTabString = fullTab.toString();
                if ((newIndent.length() >= currentIndentType.getWidth()) && newIndent.substring(newIndent.length() - currentIndentType.getWidth()).equals(fullTabString)) {
                    int i = newIndent.indexOf(fullTabString);
                    newIndent.replace(i, i + currentIndentType.getWidth(), currentIndentType.getCar().toString());
                }
                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); // Start before the first column cursor = new Position(cursor.line + 1, Position.FIRST_COLUMN - 1); 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 - 2024 Weber Informatics LLC | Privacy Policy