Skip to content

Commit 1ebe9d2

Browse files
committed
refactor: clean up code
1 parent e21d68d commit 1ebe9d2

File tree

4 files changed

+71
-24
lines changed

4 files changed

+71
-24
lines changed

src/main/java/com/github/nylle/javafixture/SpecimenFactory.java

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
import com.github.nylle.javafixture.specimen.TimeSpecimen;
1515

1616
import io.github.classgraph.ClassGraph;
17+
import io.github.classgraph.ClassInfo;
1718
import io.github.classgraph.ScanResult;
1819

1920
import java.lang.reflect.Type;
2021
import java.util.Random;
22+
import java.util.stream.Collectors;
2123
import java.util.stream.IntStream;
2224

2325
import static java.util.stream.Collectors.toMap;
@@ -93,37 +95,44 @@ public <T> ISpecimen<T> build(final SpecimenType<T> type) {
9395

9496
private <T> ISpecimen<T> implementationOrProxy(final SpecimenType<T> interfaceType) {
9597
try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
96-
var implementingClasses = scanResult.getClassesImplementing(interfaceType.asClass());
98+
var implementingClasses = scanResult.getClassesImplementing(interfaceType.asClass()).stream()
99+
.filter(x -> interfaceType.isParameterized() || isNotParametrized(x))
100+
.collect(Collectors.toList());
101+
97102
if (implementingClasses.isEmpty()) {
98103
return new InterfaceSpecimen<>(interfaceType, context, this);
99104
}
100105

101106
var implementingClass = implementingClasses.get(new Random().nextInt(implementingClasses.size()));
102-
if (implementingClass.getTypeSignature() == null || implementingClass.getTypeSignature().getTypeParameters().isEmpty()) {
107+
if (isNotParametrized(implementingClass)) {
103108
return new ObjectSpecimen<>(SpecimenType.fromClass(implementingClass.loadClass()), context, this);
104109
}
105110

106-
if (!interfaceType.isParameterized()) {
107-
return new InterfaceSpecimen<>(interfaceType, context, this);
108-
}
109-
110-
var typeParameters = IntStream.range(0, interfaceType.getGenericTypeArguments().length)
111-
.boxed()
112-
.collect(toMap(
113-
i -> interfaceType.getTypeParameterName(i),
114-
i -> SpecimenType.fromClass(interfaceType.getGenericTypeArgument(i))));
115-
116-
var actualTypeArguments = implementingClass.getTypeSignature().getTypeParameters().stream()
117-
.map(x -> typeParameters.get(x.getName()).asClass())
118-
.toArray(size -> new Type[size]);
119-
120111
return new GenericSpecimen<>(
121-
SpecimenType.fromRawType(implementingClass.loadClass(), actualTypeArguments),
112+
SpecimenType.fromRawType(implementingClass.loadClass(), resolveTypeArguments(interfaceType, implementingClass)),
122113
context,
123114
this);
124115
} catch (Exception ex) {
125116
return new InterfaceSpecimen<>(interfaceType, context, this);
126117
}
127118
}
119+
120+
private static boolean isNotParametrized(ClassInfo classInfo) {
121+
return classInfo.getTypeSignature() == null || classInfo.getTypeSignature().getTypeParameters().isEmpty();
122+
}
123+
124+
private static <T> Type[] resolveTypeArguments(SpecimenType<T> genericType, ClassInfo implementingClass) {
125+
// throws NPE if implementing class has more type arguments than interface
126+
// this was not intended, but luckily causes a fallback to proxy, because we wouldn't be able to resolve the additional type anyway
127+
var typeParameters = IntStream.range(0, genericType.getGenericTypeArguments().length)
128+
.boxed()
129+
.collect(toMap(
130+
i -> genericType.getTypeParameterName(i),
131+
i -> SpecimenType.fromClass(genericType.getGenericTypeArgument(i))));
132+
133+
return implementingClass.getTypeSignature().getTypeParameters().stream()
134+
.map(x -> typeParameters.get(x.getName()).asClass())
135+
.toArray(size -> new Type[size]);
136+
}
128137
}
129138

src/test/java/com/github/nylle/javafixture/SpecimenFactoryTest.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.github.nylle.javafixture.testobjects.interfaces.GenericInterfaceTUWithGenericImplementationT;
2121
import com.github.nylle.javafixture.testobjects.interfaces.GenericInterfaceTUWithGenericImplementationTU;
2222
import com.github.nylle.javafixture.testobjects.interfaces.GenericInterfaceTUWithGenericImplementationU;
23+
import com.github.nylle.javafixture.testobjects.interfaces.GenericInterfaceTWithGenericImplementationTU;
2324
import com.github.nylle.javafixture.testobjects.interfaces.GenericInterfaceWithImplementation;
2425
import com.github.nylle.javafixture.testobjects.interfaces.InterfaceWithGenericImplementation;
2526
import com.github.nylle.javafixture.testobjects.interfaces.InterfaceWithImplementation;
@@ -111,17 +112,24 @@ class CreatesInterfaceSpecimen {
111112

112113
@Test
113114
@DisplayName("no implementations found")
114-
void createsInterfaceSpecimenIfInterfaceHasNoImplementations() {
115+
void ifInterfaceHasNoImplementations() {
115116
assertThat(new SpecimenFactory(context).build(SpecimenType.fromClass(InterfaceWithoutImplementation.class)))
116117
.isExactlyInstanceOf(InterfaceSpecimen.class);
117118
}
118119

119120
@Test
120121
@DisplayName("implementation is generic and interface is not")
121-
void createsInterfaceSpecimenIfImplementationIsGenericAndInterfaceIsNot() {
122+
void ifImplementationIsGenericAndInterfaceIsNot() {
122123
assertThat(new SpecimenFactory(context).build(SpecimenType.fromClass(InterfaceWithGenericImplementation.class)))
123124
.isExactlyInstanceOf(InterfaceSpecimen.class);
124125
}
126+
127+
@Test
128+
@DisplayName("implementation has more type arguments than interface")
129+
void ifImplementationHasMoreTypeArgumentsThanInterface() {
130+
assertThat(new SpecimenFactory(context).build(new SpecimenType<GenericInterfaceTWithGenericImplementationTU<String>>() {}))
131+
.isExactlyInstanceOf(InterfaceSpecimen.class);
132+
}
125133
}
126134

127135
@Nested
@@ -130,14 +138,14 @@ class CreatesObjectSpecimen {
130138

131139
@Test
132140
@DisplayName("implementation is not generic and interface is not generic")
133-
void createsObjectSpecimenIfBothImplementationAndInterfaceAreNotGeneric() {
141+
void ifBothImplementationAndInterfaceAreNotGeneric() {
134142
assertThat(new SpecimenFactory(context).build(SpecimenType.fromClass(InterfaceWithImplementation.class)))
135143
.isExactlyInstanceOf(ObjectSpecimen.class);
136144
}
137145

138146
@Test
139147
@DisplayName("implementation is not generic and interface is generic")
140-
void createsObjectSpecimenIfImplementationIsNotGenericAndInterfaceIs() {
148+
void ifImplementationIsNotGenericAndInterfaceIs() {
141149
assertThat(new SpecimenFactory(context).build(new SpecimenType<GenericInterfaceWithImplementation<Integer, String>>() {}))
142150
.isExactlyInstanceOf(ObjectSpecimen.class);
143151
}
@@ -149,21 +157,21 @@ class CreatesGenericSpecimen {
149157

150158
@Test
151159
@DisplayName("implementation is generic and interface is generic")
152-
void createsGenericSpecimenIfImplementationAndInterfaceAreGeneric() {
160+
void ifImplementationAndInterfaceAreGeneric() {
153161
assertThat(new SpecimenFactory(context).build(new SpecimenType<GenericInterfaceTUWithGenericImplementationTU<String, Integer>>() {}))
154162
.isExactlyInstanceOf(GenericSpecimen.class);
155163
}
156164

157165
@Test
158166
@DisplayName("generic implementation only uses first type-argument of generic interface")
159-
void createsGenericSpecimenIfGenericImplementationOnlyUsesFirstTypeArgumentOfGenericInterface() {
167+
void ifGenericImplementationOnlyUsesFirstTypeArgumentOfGenericInterface() {
160168
assertThat(new SpecimenFactory(context).build(new SpecimenType<GenericInterfaceTUWithGenericImplementationT<String, Integer>>() {}))
161169
.isExactlyInstanceOf(GenericSpecimen.class);
162170
}
163171

164172
@Test
165173
@DisplayName("generic implementation only uses second type-argument of generic interface")
166-
void createsGenericSpecimenIfGenericImplementationOnlyUsesSecondTypeArgumentOfGenericInterface() {
174+
void ifGenericImplementationOnlyUsesSecondTypeArgumentOfGenericInterface() {
167175
assertThat(new SpecimenFactory(context).build(new SpecimenType<GenericInterfaceTUWithGenericImplementationU<String, Integer>>() {}))
168176
.isExactlyInstanceOf(GenericSpecimen.class);
169177
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.github.nylle.javafixture.testobjects.interfaces;
2+
3+
public interface GenericInterfaceTWithGenericImplementationTU<T> {
4+
int publicField = 1;
5+
6+
T getT();
7+
8+
void setT(T value);
9+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.github.nylle.javafixture.testobjects.interfaces;
2+
3+
public class GenericInterfaceTWithGenericImplementationTUImpl<T, U> implements GenericInterfaceTWithGenericImplementationTU<T> {
4+
private U u;
5+
private T t;
6+
7+
@Override
8+
public T getT() {
9+
return t;
10+
}
11+
12+
@Override
13+
public void setT(T value) {
14+
t = value;
15+
}
16+
17+
public U getU() {
18+
return u;
19+
}
20+
21+
}

0 commit comments

Comments
 (0)