org.stringtemplate.v4.AutoIndentWriter Maven / Gradle / Ivy
Show all versions of ST4 Show documentation
/*
* [The "BSD license"]
* Copyright (c) 2011 Terence Parr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.stringtemplate.v4;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* Essentially a char filter that knows how to auto-indent output by maintaining
* a stack of indent levels.
*
* The indent stack is a stack of strings so we can repeat original indent not
* just the same number of columns (don't have to worry about tabs vs spaces
* then). Anchors are char positions (tabs won't work) that indicate where all
* future wraps should justify to. The wrap position is actually the larger of
* either the last anchor or the indentation level.
*
* This is a filter on a {@link Writer}.
*
* {@code \n} is the proper way to say newline for options and templates.
* Templates can mix {@code \r\n} and {@code \n} them, but use {@code \n} in
* options like {@code wrap="\n"}. This writer will render newline characters
* according to {@link #newline}. The default value is taken from the
* {@code line.separator} system property, and can be overridden by passing in a
* {@code String} to the appropriate constructor.
*/
public class AutoIndentWriter implements STWriter {
/** Stack of indents. Use {@link List} as it's much faster than {@link Stack}. Grows
* from 0..n-1.
*/
public List indents = new ArrayList();
/** Stack of integer anchors (char positions in line); avoid {@link Integer}
* creation overhead.
*/
public int[] anchors = new int[10];
public int anchors_sp = -1;
/** {@code \n} or {@code \r\n}? */
public String newline;
public Writer out = null;
public boolean atStartOfLine = true;
/**
* Track char position in the line (later we can think about tabs). Indexed
* from 0. We want to keep {@code charPosition <= }{@link #lineWidth}.
* This is the position we are about to write, not the position
* last written to.
*/
public int charPosition = 0;
/** The absolute char index into the output of the next char to be written. */
public int charIndex = 0;
public int lineWidth = NO_WRAP;
public AutoIndentWriter(Writer out, String newline) {
this.out = out;
indents.add(null); // s oftart with no indent
this.newline = newline;
}
public AutoIndentWriter(Writer out) {
this(out, System.getProperty("line.separator"));
}
@Override
public void setLineWidth(int lineWidth) {
this.lineWidth = lineWidth;
}
@Override
public void pushIndentation(String indent) {
indents.add(indent);
}
@Override
public String popIndentation() {
return indents.remove(indents.size()-1);
}
@Override
public void pushAnchorPoint() {
if ( (anchors_sp +1)>=anchors.length ) {
int[] a = new int[anchors.length*2];
System.arraycopy(anchors, 0, a, 0, anchors.length-1);
anchors = a;
}
anchors_sp++;
anchors[anchors_sp] = charPosition;
}
@Override
public void popAnchorPoint() {
anchors_sp--;
}
@Override
public int index() { return charIndex; }
/** Write out a string literal or attribute expression or expression element. */
@Override
public int write(String str) throws IOException {
int n = 0;
int nll = newline.length();
int sl = str.length();
for (int i=0; i
* If doing line wrap, then check {@code wrap} before emitting {@code str}.
* If at or beyond desired line width then emit a {@link #newline} and any
* indentation before spitting out {@code str}.
*/
@Override
public int write(String str, String wrap) throws IOException {
int n = writeWrap(wrap);
return n + write(str);
}
@Override
public int writeWrap(String wrap) throws IOException {
int n = 0;
// if want wrap and not already at start of line (last char was \n)
// and we have hit or exceeded the threshold
if ( lineWidth!=NO_WRAP && wrap!=null && !atStartOfLine &&
charPosition >= lineWidth )
{
// ok to wrap
// Walk wrap string and look for A\nB. Spit out A\n
// then spit indent or anchor, whichever is larger
// then spit out B.
for (int i=0; i=0 && anchors[anchors_sp]>indentWidth ) {
int remainder = anchors[anchors_sp]-indentWidth;
for (int i=1; i<=remainder; i++) out.write(' ');
n += remainder;
}
charPosition += n;
charIndex += n;
return n;
}
}