Skip to content

Commit

Permalink
(victornoel#3) Handle collisions of type parameter names
Browse files Browse the repository at this point in the history
  • Loading branch information
andreoss committed Jun 21, 2020
1 parent bee8a50 commit 22ec0f2
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import java.util.Collection;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
Expand Down Expand Up @@ -95,7 +98,7 @@ public GeneratedEnvelopeTypeSpec(final TypeElement source,
public TypeSpec typeSpec() throws Exception {
final GenerateEnvelope annotation = this.source.getAnnotation(GenerateEnvelope.class);
final TypeName spr = TypeName.get(this.source.asType());
final TypeVariableName type = TypeVariableName.get("W", spr);
final TypeVariableName type = TypeVariableName.get(this.genericTypeName(), spr);
final TypeName prm;
if (annotation.generic()) {
prm = type;
Expand Down Expand Up @@ -138,6 +141,59 @@ public TypeSpec typeSpec() throws Exception {
return builder.build();
}

/**
* Generic type parameter.
*
* @return Type parameter name
*/
private String genericTypeName() {
final Set<String> occupied = Stream.of(
this.source.getTypeParameters()
.stream(),
this.localAndInheritedMethods()
.stream()
.flatMap(x -> x.getTypeParameters().stream())
)
.flatMap(Function.identity())
.map(x -> x.getSimpleName().toString())
.collect(Collectors.toSet());
return this.genericTypeName("W", occupied);
}

/**
* Local and inherited methods.
*
* @return Set of methods.
*/
private Set<ExecutableElement> localAndInheritedMethods() {
return MoreElements.getLocalAndInheritedMethods(
this.source, this.procenv.getTypeUtils(), this.procenv.getElementUtils()
);
}

/**
* Generic type parameter.
*
* @param preferred Preferred name
* @param occupied Names already in use
* @return Type parameter name
*/
private String genericTypeName(final String preferred, final Set<String> occupied) {
final String result;
if (occupied.contains(preferred)) {
final char[] chars = preferred.toCharArray();
chars[chars.length - 1] -= 1;
if (chars[chars.length - 1] < 'A') {
result = this.genericTypeName(preferred + preferred, occupied);
} else {
result = this.genericTypeName(String.valueOf(chars), occupied);
}
} else {
result = preferred;
}
return result;
}

/**
* Generated delegating methods.
*
Expand All @@ -163,11 +219,7 @@ private final class DelegatingMethods
*/
DelegatingMethods(final FieldSpec wrapped) {
this(
MoreElements.getLocalAndInheritedMethods(
GeneratedEnvelopeTypeSpec.this.source,
GeneratedEnvelopeTypeSpec.this.procenv.getTypeUtils(),
GeneratedEnvelopeTypeSpec.this.procenv.getElementUtils()
),
GeneratedEnvelopeTypeSpec.this.localAndInheritedMethods(),
wrapped
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,115 @@ public void samInterfaceWithGenericEnvelopeAndGenericParameter() {
)
);
}

@Test
public void samInterfaceHandlesMultipleCollisionsTwoLetters() {
final Compilation compilation = Compiler.javac()
.withProcessors(new GenerateEnvelopeProcessor())
.compile(
JavaFileObjects.forSourceLines(
"Foo",
"import com.github.victornoel.eo.GenerateEnvelope;",
"import java.util.function.Supplier;",
"@GenerateEnvelope(generic = true)",
"public interface Foo<W> { <V> W get(V x); };"
)
);
CompilationSubject.assertThat(compilation).succeededWithoutWarnings();
CompilationSubject.assertThat(compilation)
.generatedSourceFile("FooEnvelope")
.hasSourceEquivalentTo(
JavaFileObjects.forSourceLines(
"FooEnvelope",
"import java.lang.Override;",
"import javax.annotation.Generated;",
// @checkstyle LineLengthCheck (1 line)
"@Generated(\"com.github.victornoel.eo.apt.GenerateEnvelopeProcessor\")",
"public abstract class FooEnvelope<W, U extends Foo<W>> implements Foo<W> {",
" protected final U wrapped;",
" public FooEnvelope(U wrapped) {",
" this.wrapped = wrapped;",
" }",
" @Override",
" public final <V> W get(V x) { ",
" return wrapped.get(x);",
" }",
"}"
)
);
}

@Test
public void samInterfaceHandlesMultipleCollisions() {
final Compilation compilation = Compiler.javac()
.withProcessors(new GenerateEnvelopeProcessor())
.compile(
JavaFileObjects.forSourceLines(
"Foo",
"import com.github.victornoel.eo.GenerateEnvelope;",
"import java.util.function.Supplier;",
"@GenerateEnvelope(generic = true)",
"public interface Foo<W> { <V> W get(V x); };"
)
);
CompilationSubject.assertThat(compilation).succeededWithoutWarnings();
CompilationSubject.assertThat(compilation)
.generatedSourceFile("FooEnvelope")
.hasSourceEquivalentTo(
JavaFileObjects.forSourceLines(
"FooEnvelope",
"import java.lang.Override;",
"import javax.annotation.Generated;",
// @checkstyle LineLengthCheck (1 line)
"@Generated(\"com.github.victornoel.eo.apt.GenerateEnvelopeProcessor\")",
"public abstract class FooEnvelope<W, U extends Foo<W>> implements Foo<W> {",
" protected final U wrapped;",
" public FooEnvelope(U wrapped) {",
" this.wrapped = wrapped;",
" }",
" @Override",
" public final <V> W get(V x) { ",
" return wrapped.get(x);",
" }",
"}"
)
);
}

@Test
public void samInterfaceHandlesCollisionOfTypeParameters() {
final Compilation compilation = Compiler.javac()
.withProcessors(new GenerateEnvelopeProcessor())
.compile(
JavaFileObjects.forSourceLines(
"Foo",
"import com.github.victornoel.eo.GenerateEnvelope;",
"import java.util.function.Supplier;",
"@GenerateEnvelope(generic = true)",
"public interface Foo<W> extends Supplier<W> { };"
)
);
CompilationSubject.assertThat(compilation).succeededWithoutWarnings();
CompilationSubject.assertThat(compilation)
.generatedSourceFile("FooEnvelope")
.hasSourceEquivalentTo(
JavaFileObjects.forSourceLines(
"FooEnvelope",
"import java.lang.Override;",
"import javax.annotation.Generated;",
// @checkstyle LineLengthCheck (1 line)
"@Generated(\"com.github.victornoel.eo.apt.GenerateEnvelopeProcessor\")",
"public abstract class FooEnvelope<W, V extends Foo<W>> implements Foo<W> {",
" protected final V wrapped;",
" public FooEnvelope(V wrapped) {",
" this.wrapped = wrapped;",
" }",
" @Override",
" public final W get() {",
" return wrapped.get();",
" }",
"}"
)
);
}
}

0 comments on commit 22ec0f2

Please sign in to comment.