blob: bf177a0bc1cf3d8ecc9cc8bf99006df821952eaf [file] [log] [blame]
/*
* Copyright (C) 2016 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.javapoet;
import java.io.IOException;
import static com.squareup.javapoet.Util.checkNotNull;
/**
* Implements soft line wrapping on an appendable. To use, append characters using {@link #append}
* or soft-wrapping spaces using {@link #wrappingSpace}.
*/
final class LineWrapper {
private final Appendable out;
private final String indent;
private final int columnLimit;
private boolean closed;
/** Characters written since the last wrapping space that haven't yet been flushed. */
private final StringBuilder buffer = new StringBuilder();
/** The number of characters since the most recent newline. Includes both out and the buffer. */
private int column = 0;
/** -1 if we have no buffering; otherwise the number of spaces to write after wrapping. */
private int indentLevel = -1;
LineWrapper(Appendable out, String indent, int columnLimit) {
checkNotNull(out, "out == null");
this.out = out;
this.indent = indent;
this.columnLimit = columnLimit;
}
/** Emit {@code s}. This may be buffered to permit line wraps to be inserted. */
void append(String s) throws IOException {
if (closed) throw new IllegalStateException("closed");
if (indentLevel != -1) {
int nextNewline = s.indexOf('\n');
// If s doesn't cause the current line to cross the limit, buffer it and return. We'll decide
// whether or not we have to wrap it later.
if (nextNewline == -1 && column + s.length() <= columnLimit) {
buffer.append(s);
column += s.length();
return;
}
// Wrap if appending s would overflow the current line.
boolean wrap = nextNewline == -1 || column + nextNewline > columnLimit;
flush(wrap);
}
out.append(s);
int lastNewline = s.lastIndexOf('\n');
column = lastNewline != -1
? s.length() - lastNewline - 1
: column + s.length();
}
/** Emit either a space or a newline character. */
void wrappingSpace(int indentLevel) throws IOException {
if (closed) throw new IllegalStateException("closed");
if (this.indentLevel != -1) flush(false);
this.column++;
this.indentLevel = indentLevel;
}
/** Flush any outstanding text and forbid future writes to this line wrapper. */
void close() throws IOException {
if (indentLevel != -1) flush(false);
closed = true;
}
/** Write the space followed by any buffered text that follows it. */
private void flush(boolean wrap) throws IOException {
if (wrap) {
out.append('\n');
for (int i = 0; i < indentLevel; i++) {
out.append(indent);
}
column = indentLevel * indent.length();
column += buffer.length();
} else {
out.append(' ');
}
out.append(buffer);
buffer.delete(0, buffer.length());
indentLevel = -1;
}
}