Skip to content
This repository has been archived by the owner on Oct 10, 2024. It is now read-only.

fix #Issue490 #903

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/main/java/com/squareup/javapoet/CodeBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/squareup/javapoet/CodeWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -504,6 +507,7 @@ CodeWriter emitAndIndent(String s) throws IOException {
out.append(line);
trailingNewline = false;
}

return this;
}

Expand Down
283 changes: 155 additions & 128 deletions src/main/java/com/squareup/javapoet/LineWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}


}
6 changes: 6 additions & 0 deletions src/main/java/com/squareup/javapoet/TypeName.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<TypeParameterElement, TypeVariableName> typeVariables) {
return mirror.accept(new SimpleTypeVisitor8<TypeName, Void>() {
Expand Down
8 changes: 8 additions & 0 deletions src/test/java/com/squareup/javapoet/CodeBlockTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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("()");
}
}
14 changes: 14 additions & 0 deletions src/test/java/com/squareup/javapoet/FieldSpecTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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");
}
}