CurlyPrinter.java
package com.mackenziehigh.autumn.lang.compiler.pretty;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.mackenziehigh.autumn.resources.Finished;
import java.util.LinkedList;
/**
* An instance of this class aids in the pretty-printing of curly-bracket style languages.
*
* @author Mackenzie High
*/
@Finished("2014/07/12")
final class CurlyPrinter
{
private static enum LineTypes
{
BRACKET_OPEN,
BRACKET_CLOSE,
STRING,
EMPTY_LINE,
}
/**
* This is the current indentation level.
*/
private int indent = 0;
/**
* The nth element of this list is the type of information on the nth line.
*/
private final LinkedList<LineTypes> line_types = Lists.newLinkedList();
/**
* The nth element of this list is the text of the nth line.
*/
private final LinkedList<StringBuilder> line_texts = Lists.newLinkedList();
/**
* This method begins printing a new non-empty line.
*/
public void addLine()
{
final StringBuilder line = new StringBuilder();
line.append(buildIndent());
line_types.add(LineTypes.STRING);
line_texts.add(line);
}
/**
* This method adds a line that is deliberately empty.
*
* <p>
* This method will ignore an invocation, if it would result in a duplicate empty line.
* </p>
*/
public void addEmptyLine()
{
if (!line_types.isEmpty() && line_types.getLast() == LineTypes.EMPTY_LINE)
{
return;
}
line_types.add(LineTypes.EMPTY_LINE);
line_texts.add(new StringBuilder());
}
/**
* This method adds an opening bracket that starts an indented region.
*/
public void addOpeningBracket()
{
final StringBuilder line = new StringBuilder();
line.append(buildIndent());
line.append("{");
line_types.add(LineTypes.BRACKET_OPEN);
line_texts.add(line);
++indent;
}
/**
* This method adds a closing bracket that terminates an indented region.
*/
public void addClosingBracket()
{
--indent;
final StringBuilder line = new StringBuilder();
line.append(buildIndent());
line.append("}");
line_types.add(LineTypes.BRACKET_CLOSE);
line_texts.add(line);
}
/**
* This method adds a string of text to the current line.
*
* @param value is the text to add.
* @throws IllegalStateException if the current line cannot be written to.
*/
public void addText(final Object value)
{
assert line_types.size() == line_texts.size();
Preconditions.checkState(line_types.isEmpty() == false);
Preconditions.checkState(line_types.getLast() == LineTypes.STRING);
line_texts.get(line_texts.size() - 1).append(value);
}
/**
* If the last lines added were empty-lines, then remove them; otherwise, do nothing.
*/
public void removeEmptyLines()
{
while (!line_types.isEmpty() && line_types.getLast() == LineTypes.EMPTY_LINE)
{
line_types.removeLast();
line_texts.removeLast();
}
}
/**
* This method creates an indentation string.
*
* @return (4 * indent) number of space characters.
*/
private String buildIndent()
{
return Strings.repeat(" ", indent * 4);
}
/**
* This method creates a string representation of this object.
*
* <p>
* Note: Calling this method resets the internal state of this object.
* </p>
*
* @return a pretty printable string.
*/
public String buildString()
{
// Remove any leading empty-lines.
while (!line_types.isEmpty() && line_types.getFirst() == LineTypes.EMPTY_LINE)
{
line_types.removeFirst();
line_texts.removeFirst();
}
// Remove any trailing empty-lines.
while (!line_types.isEmpty() && line_types.getLast() == LineTypes.EMPTY_LINE)
{
line_types.removeLast();
line_texts.removeLast();
}
final StringBuilder result = new StringBuilder();
for (StringBuilder line : line_texts)
{
result.append(line.toString());
result.append('\n');
}
// Reset Everything
this.indent = 0;
this.line_types.clear();
this.line_texts.clear();
return result.toString();
}
}