diff --git a/src/main/java/com/squareup/javapoet/CodeBlock.java b/src/main/java/com/squareup/javapoet/CodeBlock.java index 02542f58b..521610c5d 100644 --- a/src/main/java/com/squareup/javapoet/CodeBlock.java +++ b/src/main/java/com/squareup/javapoet/CodeBlock.java @@ -91,10 +91,13 @@ public boolean isEmpty() { return toString().hashCode(); } - @Override public String toString() { + @Override public String + toString() { StringBuilder out = new StringBuilder(); try { - new CodeWriter(out).emit(this); + CodeWriter codeWriter = new CodeWriter(out); + codeWriter.emit(this); + codeWriter.flush(); return out.toString(); } catch (IOException e) { throw new AssertionError(); @@ -357,6 +360,7 @@ private TypeName argToType(Object o) { if (o instanceof TypeMirror) return TypeName.get((TypeMirror) o); if (o instanceof Element) return TypeName.get(((Element) o).asType()); if (o instanceof Type) return TypeName.get((Type) o); + if (o instanceof FieldSpec) return TypeName.get((FieldSpec) o); throw new IllegalArgumentException("expected type but was " + o); } diff --git a/src/main/java/com/squareup/javapoet/CodeWriter.java b/src/main/java/com/squareup/javapoet/CodeWriter.java index da6d3c8e5..446c04479 100644 --- a/src/main/java/com/squareup/javapoet/CodeWriter.java +++ b/src/main/java/com/squareup/javapoet/CodeWriter.java @@ -323,6 +323,9 @@ public CodeWriter emit(CodeBlock codeBlock, boolean ensureTrailingNewline) throw } return this; } + public void flush() throws IOException { + out.unclosedFlush(); + } public CodeWriter emitWrappingSpace() throws IOException { out.wrappingSpace(indentLevel + 2); @@ -504,6 +507,7 @@ CodeWriter emitAndIndent(String s) throws IOException { out.append(line); trailingNewline = false; } + return this; } diff --git a/src/main/java/com/squareup/javapoet/LineWrapper.java b/src/main/java/com/squareup/javapoet/LineWrapper.java index 928d9f4ed..9494f7264 100644 --- a/src/main/java/com/squareup/javapoet/LineWrapper.java +++ b/src/main/java/com/squareup/javapoet/LineWrapper.java @@ -24,148 +24,175 @@ * or soft-wrapping spaces using {@link #wrappingSpace}. */ final class LineWrapper { - private final RecordingAppendable 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 {@code indent}s to write after wrapping. - */ - private int indentLevel = -1; - - /** - * Null if we have no buffering; otherwise the type to pass to the next call to {@link #flush}. - */ - private FlushType nextFlush; - - LineWrapper(Appendable out, String indent, int columnLimit) { - checkNotNull(out, "out == null"); - this.out = new RecordingAppendable(out); - this.indent = indent; - this.columnLimit = columnLimit; - } - - /** @return the last emitted char or {@link Character#MIN_VALUE} if nothing emitted yet. */ - char lastChar() { - return out.lastChar; - } - - /** 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 (nextFlush != null) { - 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 ? FlushType.WRAP : nextFlush); + private final RecordingAppendable 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 {@code indent}s to write after wrapping. + */ + private int indentLevel = -1; + + /** + * Null if we have no buffering; otherwise the type to pass to the next call to {@link #flush}. + */ + private FlushType nextFlush; + + LineWrapper(Appendable out, String indent, int columnLimit) { + checkNotNull(out, "out == null"); + this.out = new RecordingAppendable(out); + this.indent = indent; + this.columnLimit = columnLimit; } - 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.nextFlush != null) flush(nextFlush); - column++; // Increment the column even though the space is deferred to next call to flush(). - this.nextFlush = FlushType.SPACE; - this.indentLevel = indentLevel; - } - - /** Emit a newline character if the line will exceed it's limit, otherwise do nothing. */ - void zeroWidthSpace(int indentLevel) throws IOException { - if (closed) throw new IllegalStateException("closed"); - - if (column == 0) return; - if (this.nextFlush != null) flush(nextFlush); - this.nextFlush = FlushType.EMPTY; - this.indentLevel = indentLevel; - } - - /** Flush any outstanding text and forbid future writes to this line wrapper. */ - void close() throws IOException { - if (nextFlush != null) flush(nextFlush); - closed = true; - } - - /** Write the space followed by any buffered text that follows it. */ - private void flush(FlushType flushType) throws IOException { - switch (flushType) { - case WRAP: - out.append('\n'); - for (int i = 0; i < indentLevel; i++) { - out.append(indent); + /** + * @return the last emitted char or {@link Character#MIN_VALUE} if nothing emitted yet. + */ + char lastChar() { + return out.lastChar; + } + + /** + * 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 (nextFlush != null) { + 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 ? FlushType.WRAP : nextFlush); } - column = indentLevel * indent.length(); - column += buffer.length(); - break; - case SPACE: - out.append(' '); - break; - case EMPTY: - break; - default: - throw new IllegalArgumentException("Unknown FlushType: " + flushType); + + 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.nextFlush != null) flush(nextFlush); + column++; // Increment the column even though the space is deferred to next call to flush(). + this.nextFlush = FlushType.SPACE; + this.indentLevel = indentLevel; } - out.append(buffer); - buffer.delete(0, buffer.length()); - indentLevel = -1; - nextFlush = null; - } + /** + * Emit a newline character if the line will exceed it's limit, otherwise do nothing. + */ + void zeroWidthSpace(int indentLevel) throws IOException { + if (closed) throw new IllegalStateException("closed"); - private enum FlushType { - WRAP, SPACE, EMPTY; - } + if (column == 0) return; + if (this.nextFlush != null) flush(nextFlush); + this.nextFlush = FlushType.EMPTY; + this.indentLevel = indentLevel; + } - /** A delegating {@link Appendable} that records info about the chars passing through it. */ - static final class RecordingAppendable implements Appendable { - private final Appendable delegate; + /** + * Flush any outstanding text and forbid future writes to this line wrapper. + */ + void close() throws IOException { + if (nextFlush != null) flush(nextFlush); + closed = true; + } - char lastChar = Character.MIN_VALUE; + /** + * Write the space followed by any buffered text that follows it. + */ + private void flush(FlushType flushType) throws IOException { + switch (flushType) { + case WRAP: + out.append('\n'); + for (int i = 0; i < indentLevel; i++) { + out.append(indent); + } + column = indentLevel * indent.length(); + column += buffer.length(); + break; + case SPACE: + out.append(' '); + break; + case EMPTY: + break; + default: + throw new IllegalArgumentException("Unknown FlushType: " + flushType); + } - RecordingAppendable(Appendable delegate) { - this.delegate = delegate; + out.append(buffer); + buffer.delete(0, buffer.length()); + indentLevel = -1; + nextFlush = null; } - @Override public Appendable append(CharSequence csq) throws IOException { - int length = csq.length(); - if (length != 0) { - lastChar = csq.charAt(length - 1); - } - return delegate.append(csq); + protected void unclosedFlush() throws IOException { + if (nextFlush != null) flush(nextFlush); } - @Override public Appendable append(CharSequence csq, int start, int end) throws IOException { - CharSequence sub = csq.subSequence(start, end); - return append(sub); + private enum FlushType { + WRAP, SPACE, EMPTY; } - @Override public Appendable append(char c) throws IOException { - lastChar = c; - return delegate.append(c); + /** + * A delegating {@link Appendable} that records info about the chars passing through it. + */ + static final class RecordingAppendable implements Appendable { + private final Appendable delegate; + + char lastChar = Character.MIN_VALUE; + + RecordingAppendable(Appendable delegate) { + this.delegate = delegate; + } + + @Override + public Appendable append(CharSequence csq) throws IOException { + int length = csq.length(); + if (length != 0) { + lastChar = csq.charAt(length - 1); + } + return delegate.append(csq); + } + + @Override + public Appendable append(CharSequence csq, int start, int end) throws IOException { + CharSequence sub = csq.subSequence(start, end); + return append(sub); + } + + @Override + public Appendable append(char c) throws IOException { + lastChar = c; + return delegate.append(c); + } } - } + + } diff --git a/src/main/java/com/squareup/javapoet/TypeName.java b/src/main/java/com/squareup/javapoet/TypeName.java index c0986bb99..c8d3c02ec 100644 --- a/src/main/java/com/squareup/javapoet/TypeName.java +++ b/src/main/java/com/squareup/javapoet/TypeName.java @@ -243,6 +243,12 @@ public static TypeName get(TypeMirror mirror) { return get(mirror, new LinkedHashMap<>()); } + public static TypeName get(FieldSpec spec){ + return spec.type; + } + + + static TypeName get(TypeMirror mirror, final Map typeVariables) { return mirror.accept(new SimpleTypeVisitor8() { diff --git a/src/test/java/com/squareup/javapoet/CodeBlockTest.java b/src/test/java/com/squareup/javapoet/CodeBlockTest.java index 11b75fa4f..59cb736fd 100644 --- a/src/test/java/com/squareup/javapoet/CodeBlockTest.java +++ b/src/test/java/com/squareup/javapoet/CodeBlockTest.java @@ -348,4 +348,12 @@ public final class CodeBlockTest { assertThat(block.toString()).isEmpty(); } + @Test public void dollarSignTruncated (){ + CodeBlock block = + CodeBlock.builder() + .add(CodeBlock.of("($>$Z")) + .add(CodeBlock.of(")$<")) + .build(); + assertThat(block.toString()).isEqualTo("()"); + } } diff --git a/src/test/java/com/squareup/javapoet/FieldSpecTest.java b/src/test/java/com/squareup/javapoet/FieldSpecTest.java index bc68f6fb6..f4601d274 100644 --- a/src/test/java/com/squareup/javapoet/FieldSpecTest.java +++ b/src/test/java/com/squareup/javapoet/FieldSpecTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.fail; import javax.lang.model.element.Modifier; +import java.util.ArrayList; public class FieldSpecTest { @Test public void equalsAndHashCode() { @@ -63,4 +64,17 @@ public class FieldSpecTest { builder.modifiers.remove(1); assertThat(builder.build().modifiers).containsExactly(Modifier.PUBLIC); } + @Test public void testIssue490(){ + FieldSpec spec = FieldSpec.builder(float.class, "foo").build(); + CodeBlock test =CodeBlock.builder().add("$T temp= $L", spec,1f).build(); + assertThat(test.toString()).isEqualTo("float temp= 1.0"); + } + @Test public void testIssue490_2(){ + FieldSpec spec = FieldSpec.builder(float.class, "foo").build(); + MethodSpec test = MethodSpec.methodBuilder("test") + .returns(spec.type) + .addStatement("$T temp= $L", spec,1f) + .build(); + assertThat(test.toString()).contains("float temp= 1.0"); + } }