/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2015, MontiCore, All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* ******************************************************************************
*/
package de.monticore.prettyprint;
/**
* This class can be used as a printer to files or StringBuilders. It adds
* indentation by using the special methods indent(), unindent(). The method
* calls indent() and reindent() affect the current line, even if the first part
* was already printed. Note: Call flushBuffer() at the end in order to flush
* the internal buffer to retrieve the correct output.
* STATE Ok; Useful class for indent strings
*/
public class IndentPrinter {
// Current level opf indentation
protected int indent = 0;
// Currrent identation as String
protected String spacer = "";
// length of one indent:
// default length is 2
protected String sp = " ";
// line length
protected int maxlinelength = -1;
// optional break (makes additional line breal only after
// optionalBreak()-invocation
protected boolean optionalBreak = false;
private int optionalBreakPosition = -1;
// StringBuilder for for content that can still be moved by indent/unindent
protected StringBuilder linebuffer = new StringBuilder();
// StringBuilder for already correctly indented content
protected StringBuilder writtenbuffer;
/**
* Uses a new interal buffer for writing
*/
public IndentPrinter() {
this(new StringBuilder());
}
/**
* Uses this StringBuilder for appending
*
* @param writtenbuffer Buffer to use
*/
public IndentPrinter(StringBuilder writtenbuffer) {
this.writtenbuffer = writtenbuffer;
}
/**
* Uses startcontent as start of buffer an sets the indention
*
* @param startContent first line of content
* @param indention first indention (e.g. 0 for classes, 1 for methods and
* attributes)
*/
public IndentPrinter(String startContent, int indention) {
this();
indent(indention);
addLine(startContent);
}
/**
* Returns the content of the internal buffer Note: This method isn't side
* effect free: It flushes the internal buffer. After calling this method, new
* text added by print/println is automaticly starting in a new line
*
* @return Content of buffer as String
*/
public String getContent() {
flushBuffer();
return writtenbuffer.toString();
}
/**
* Set length of intendation: number of spaces per level
*
* @param l number of spaces per level (default is 2)
*/
public void setIndentLength(int l) {
sp = "";
for (int i = 0; i < l; i++) {
sp += " ";
}
spacer = "";
for (int i = 0; i < indent; i++) {
spacer += sp;
}
}
/**
* Returns length of intendation: number of spaces per level
*
* @return number of spaces per level
*/
public int getIndentLength() {
return sp.length();
}
/**
* This method actually does the print. It deals with "\r""\n","\r","\n" in
* the string. This method is not meant for external use.
*
* @param String String to be
*/
protected void doPrint(String s) {
// get position of "\n"
int pos = s.indexOf("\n");
while (pos >= 0) {
String substring = s.substring(0, pos);
// Start new line if string exceeds maxlinelength
if (maxlinelength != -1) {
if (pos + linebuffer.length() > maxlinelength) {
handleOptionalBreak();
}
}
// Print up to newline, then a newline and new spacer
linebuffer.append(substring);
flushBuffer();
writtenbuffer.append("\n");
s = s.substring(pos + 1);
pos = s.indexOf("\n");
}
// Start new line if string exceeds maxlinelength
if (maxlinelength != -1) {
if (s.length() + linebuffer.length() > maxlinelength) {
handleOptionalBreak();
}
}
linebuffer.append(s);
}
private void handleOptionalBreak() {
if (optionalBreak) {
if (optionalBreakPosition > 0) {
String sub2 = linebuffer.substring(optionalBreakPosition);
linebuffer = linebuffer.delete(optionalBreakPosition, linebuffer.length());
flushBuffer();
writtenbuffer.append("\n");
linebuffer.append(sub2);
}
}
else {
flushBuffer();
writtenbuffer.append("\n");
}
}
/**
* Flushes the internal buffer. After calling this method, new text added by
* print/println is automaticly starting in a new line
*/
public void flushBuffer() {
optionalBreakPosition = 0;
// HK: Live with trailing spaces, as comments keep on changing otherwise
// prune trailing spaces
// int i = linebuffer.length() - 1;
// while (i >= 0 && linebuffer.charAt(i) == ' ')
// i--;
// linebuffer.setLength(i + 1);
// indent nonempty buffers
if (linebuffer.length() != 0) {
writtenbuffer.append(spacer);
writtenbuffer.append(linebuffer);
}
linebuffer.setLength(0);
}
/**
* For positive values of i: indent i levels For negative values of i:
* unindent -i levels This method call affects the current line, even if the
* first part was already printed.
*
* @param i Number of levels to indent/unindent
*/
public void indent(int i) {
if (i > 0) {
indent += i;
for (int j = 0; j < i; j++)
spacer += sp;
}
else if (i < 0) {
while (i < 0 && indent > 0) {
this.indent--;
spacer = spacer.substring(sp.length());
i++;
}
}
}
/**
* Indent one level This method call affects the current line, even if the
* first part was already printed.
*/
public void indent() {
this.indent++;
spacer += sp;
}
/**
* Unindent one level This method call affects the current line, even if the
* first part was already printed.
*/
public void unindent() {
if (this.indent > 0) {
this.indent--;
spacer = spacer.substring(sp.length());
}
}
/**
* Prints the result of the toString() method of Object o or the string "null"
* if o has the null value
*
* @param o Object to be printed
*/
public void print(Object o) {
doPrint((o == null) ? "null" : o.toString());
}
public void printWithoutProcessing(Object o) {
linebuffer.append(o.toString());
}
/**
* Prints the result of the toString() method of Object o or the string "null"
* if o has the null value followed by a newline
*
* @param o Object to be printed
*/
public void println(Object o) {
print(o);
println();
}
/**
* Prints a newline
*/
public void println() {
doPrint("\n");
}
/**
* Prints n newlines
*
* @param n Number of newlines
*/
public void println(int n) {
for (int i = 0; i < n; i++) {
doPrint("\n");
}
}
/**
* Returns the WrittenBuffer without flushing the buffer. This methdo call is
* side effect free, but might not contain the whole buffer. To retrieve the
* complete buffer call flushBuffer() before getWrtttenBuffer()
*
* @return Buffer with already written lines
*/
public StringBuilder getWrittenbuffer() {
return writtenbuffer;
}
/**
* Returns true if the current position is the beginning of a new line
*
* @return true if current line is empty
*/
public boolean isStartOfLine() {
return (linebuffer.length() == 0);
}
/**
* adds a line and sets the indention automatically as following: if more "}"
* than "{" in newContent, the next line will be unindented by the difference,
* otherwise the current line will be indented by the difference. If the
* difference is 0, no indention will be changed
*
* @param newContent content to add
*/
public void addLine(Object newContent) {
String nc = newContent.toString().trim();
int counter = 0;
for (int i = 0; i < nc.length(); i++) {
if (nc.charAt(i) == '{') {
counter++;
}
else if (nc.charAt(i) == '}') {
counter--;
}
}
if (counter < 0) {
indent(counter);
}
print(newContent);
println();
if (counter > 0) {
indent(counter);
}
}
/**
* Returns the maximum used line length
*
* @return
*/
public int getMaxlinelength() {
return maxlinelength;
}
/**
* Sets the maximum used line length
*
* @param maxlinelength
*/
public void setMaxlinelength(int maxlinelength) {
this.maxlinelength = maxlinelength;
}
/**
* optional break (makes additional line breal only after
* optionalBreak()-invocation
*
* @return
*/
public boolean isOptionalBreak() {
return optionalBreak;
}
/**
* optional break (makes additional line breal only after
* optionalBreak()-invocation
*
* @param optionBreak
*/
public void setOptionalBreak(boolean optionBreak) {
this.optionalBreak = optionBreak;
}
public void optionalBreak() {
this.optionalBreakPosition = linebuffer.length();
}
public void clearBuffer() {
flushBuffer();
writtenbuffer.setLength(0);
}
}