JavaParser Source Viewer

Home|JavaParser/com/github/javaparser/printer/SourcePrinter.java
1/*
2 * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser.
3 * Copyright (C) 2011, 2013-2020 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.printer;
23
24import com.github.javaparser.Position;
25import com.github.javaparser.printer.PrettyPrinterConfiguration.IndentType;
26import com.github.javaparser.utils.Utils;
27
28import java.util.Deque;
29import java.util.LinkedList;
30
31/**
32 * A support class for code that outputs formatted source code.
33 */
34public class SourcePrinter {
35    private final String endOfLineCharacter;
36    private final String indentation;
37    private final int tabWidth;
38    private final IndentType indentType;
39
40    private final Deque<Stringindents = new LinkedList<>();
41    private final Deque<StringreindentedIndents = new LinkedList<>();
42    private String lastPrintedIndent = "";
43    private final StringBuilder buf = new StringBuilder();
44    private Position cursor = new Position(Position.FIRST_LINEPosition.FIRST_COLUMN - 1); // Start before the first column
45    private boolean indented = false;
46
47    SourcePrinter() {
48        this(new PrettyPrinterConfiguration());
49    }
50
51    SourcePrinter(final PrettyPrinterConfiguration configuration) {
52        indentation = configuration.getIndent();
53        endOfLineCharacter = configuration.getEndOfLineCharacter();
54        tabWidth = configuration.getTabWidth();
55        indentType = configuration.getIndentType();
56        indents.push("");
57    }
58
59    /**
60     * Add the default indentation to the current indentation and push it on the indentation stack.
61     * Does not actually output anything.
62     */
63    public SourcePrinter indent() {
64        String currentIndent = indents.peek();
65        switch (indentType) {
66            case SPACES:
67            case TABS_WITH_SPACE_ALIGN:
68                indents.push(currentIndent + indentation);
69                break;
70
71            case TABS:
72                indents.push(indentation + currentIndent);
73                break;
74
75            default:
76                throw new AssertionError("Unhandled indent type");
77        }
78        return this;
79    }
80
81    /**
82     * Add to the current indentation until it is reaches "column" and push it on the indentation stack.
83     * Does not actually output anything.
84     */
85    public SourcePrinter indentWithAlignTo(int column) {
86        indents.push(calculateIndentWithAlignTo(column));
87        return this;
88    }
89
90    private String calculateIndentWithAlignTo(int column) {
91        if (column < lastPrintedIndent.length()){
92            throw new IllegalStateException("Attempt to indent less than the previous indent.");
93        }
94
95        StringBuilder newIndent = new StringBuilder(lastPrintedIndent);
96        switch (indentType) {
97            case SPACES:
98            case TABS_WITH_SPACE_ALIGN:
99                while (newIndent.length() < column) {
100                    newIndent.append(' ');
101                }
102                break;
103
104            case TABS:
105                int logicalIndentLength = newIndent.length();
106                while ((logicalIndentLength + tabWidth) <= column) {
107                    newIndent.insert(0'\t');
108                    logicalIndentLength += tabWidth;
109                }
110                while (logicalIndentLength < column) {
111                    newIndent.append(' ');
112                    logicalIndentLength++;
113                }
114                StringBuilder fullTab = new StringBuilder();
115                for(int i=0i<tabWidthi++){
116                    fullTab.append(' ');
117                }
118                String fullTabString = fullTab.toString();
119                if ((newIndent.length() >= tabWidth)
120                        && newIndent.substring(newIndent.length() - tabWidth).equals(fullTabString)) {
121                    int i = newIndent.indexOf(fullTabString);
122                    newIndent.replace(ii + tabWidth"\t");
123                }
124                break;
125
126            default:
127                throw new AssertionError("Unhandled indent type");
128        }
129
130        return newIndent.toString();
131    }
132
133    /**
134     * Pop the last indentation of the indentation stack.
135     * Does not actually output anything.
136     */
137    public SourcePrinter unindent() {
138        if (indents.isEmpty()) {
139            // Since we start out with an empty indent on the stack, this will only occur
140            // the second time we over-unindent.
141            throw new IllegalStateException("Indent/unindent calls are not well-balanced.");
142        }
143        indents.pop();
144        return this;
145    }
146
147    private void append(String arg) {
148        buf.append(arg);
149        cursor = cursor.withColumn(cursor.column + arg.length());
150    }
151
152    /**
153     * Append the source string passed as argument to the buffer.
154     * If this is being appended at the beginning of a line, performs indentation first.
155     * <p>
156     * The source line to be printed should not contain newline/carriage-return characters;
157     * use {@link #println(String)} to automatically append a newline at the end of the source string.
158     * If the source line passed as argument contains newline/carriage-return characters would
159     * impredictably affect a correct computation of the current {@link #getCursor()} position.
160     *
161     * @param arg source line to be printed (should not contain newline/carriage-return characters)
162     * @return this instance, for nesting calls to method as fluent interface
163     * @see SourcePrinter#println(String)
164     */
165    public SourcePrinter print(final String arg) {
166        if (!indented) {
167            lastPrintedIndent = indents.peek();
168            append(lastPrintedIndent);
169            indented = true;
170        }
171        append(arg);
172        return this;
173    }
174
175    /**
176     * Append the source string passed as argument to the buffer, then append a newline.
177     * If this is being appended at the beginning of a line, performs indentation first.
178     * <p>
179     * The source line to be printed should not contain newline/carriage-return characters.
180     * If the source line passed as argument contains newline/carriage-return characters would
181     * impredictably affect a correct computation of the current {@link #getCursor()} position.
182     *
183     * @param arg source line to be printed (should not contain newline/carriage-return characters)
184     * @return this instance, for nesting calls to method as fluent interface
185     */
186    public SourcePrinter println(final String arg) {
187        print(arg);
188        println();
189        return this;
190    }
191
192    /**
193     * Append a newline to the buffer.
194     *
195     * @return this instance, for nesting calls to method as fluent interface
196     */
197    public SourcePrinter println() {
198        buf.append(endOfLineCharacter);
199        cursor = new Position(cursor.line + 1Position.FIRST_COLUMN - 1); // Start before the first column
200        indented = false;
201        return this;
202    }
203
204    /**
205     * Return the current cursor position (line, column) in the source printer buffer.
206     * <p>
207     * Please notice in order to guarantee a correct computation of the cursor position,
208     * this printer expect the contracts of the methods {@link #print(String)} and {@link #println(String)}
209     * has been respected through all method calls, meaning the source string passed as argument to those method
210     * calls did not contain newline/carriage-return characters.
211     *
212     * @return the current cursor position (line, column).
213     */
214    public Position getCursor() {
215        return cursor;
216    }
217
218    /**
219     * @return the currently printed source code.
220     * @deprecated use toString()
221     */
222    @Deprecated
223    public String getSource() {
224        return toString();
225    }
226
227    /**
228     * @return the currently printed source code.
229     */
230    @Override
231    public String toString() {
232        return buf.toString();
233    }
234
235    /**
236     * Changes all EOL characters in "content" to the EOL character this SourcePrinter is using.
237     */
238    public String normalizeEolInTextBlock(String content) {
239        return Utils.normalizeEolInTextBlock(contentendOfLineCharacter);
240    }
241
242    /**
243     * Set the top-most indent to the column the cursor is currently in, can be undone with
244     * {@link #reindentToPreviousLevel()}. Does not actually output anything.
245     */
246    public void reindentWithAlignToCursor() {
247        String newIndent = calculateIndentWithAlignTo(cursor.column);
248        reindentedIndents.push(indents.pop());
249        indents.push(newIndent);
250    }
251
252    /**
253     * Set the top-most indent to the column the cursor was before the last {@link #reindentWithAlignToCursor()} call.
254     * Does not actually output anything.
255     */
256    public void reindentToPreviousLevel() {
257        if (reindentedIndents.isEmpty()) {
258            throw new IllegalStateException("Reindent calls are not well-balanced.");
259        }
260        indents.pop();
261        indents.push(reindentedIndents.pop());
262    }
263
264    /**
265     * Adds an indent to the top of the stack that is a copy of the current top indent.
266     * With this you announce "I'm going to indent the next line(s)" but not how far yet.
267     * Once you do know, you can pop this indent ("unindent") and indent to the right column.
268     * (Does not actually output anything.)
269     */
270    public void duplicateIndent() {
271        indents.push(indents.peek());
272    }
273}
274
MembersX
SourcePrinter:calculateIndentWithAlignTo:Block:newIndent
SourcePrinter:indent
SourcePrinter:indentWithAlignTo
SourcePrinter:calculateIndentWithAlignTo:Block:fullTab
SourcePrinter:println
SourcePrinter:tabWidth
SourcePrinter:print
SourcePrinter:append
SourcePrinter:indentType
SourcePrinter:buf
SourcePrinter:calculateIndentWithAlignTo:Block:Block:i
SourcePrinter:reindentWithAlignToCursor
SourcePrinter:indentation
SourcePrinter:SourcePrinter
SourcePrinter:duplicateIndent
SourcePrinter:lastPrintedIndent
SourcePrinter:reindentToPreviousLevel
SourcePrinter:normalizeEolInTextBlock
SourcePrinter:calculateIndentWithAlignTo:Block:logicalIndentLength
SourcePrinter:getSource
SourcePrinter:getCursor
SourcePrinter:endOfLineCharacter
SourcePrinter:unindent
SourcePrinter:reindentedIndents
SourcePrinter:calculateIndentWithAlignTo
SourcePrinter:indents
SourcePrinter:reindentWithAlignToCursor:Block:newIndent
SourcePrinter:cursor
SourcePrinter:indented
SourcePrinter:toString
SourcePrinter:indent:Block:currentIndent
SourcePrinter:calculateIndentWithAlignTo:Block:fullTabString
Members
X