Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sheath: Must differ types considering their generic types #538

Merged
merged 1 commit into from
Aug 7, 2024
Merged
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
96 changes: 70 additions & 26 deletions src/test/java/dagger/sheath/CtxWrapper.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package dagger.sheath;

import dagger.sheath.binding.BindingMethod;
import dagger.sheath.reflection.Signature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.reflect.FieldUtils;

import javax.inject.Provider;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.Consumer;

Expand All @@ -20,19 +22,68 @@ public CtxWrapper(Object ctx) {
}

public Object get(Class<?> clazz) {
try {
return Signature.of(clazz);
}

// binding by available providers
final var provider = this.findProviderFor(clazz);
if (provider != null) {
log.debug("status=beanSolved, from=Provider, beanClass={}", clazz);
return provider.getValue();
public Object get(Signature signature) {
{
final var found = findUsingProvider(signature.getClazz());
if (found != null) {
log.debug("status=foundUsingProvider");
return found;
}
}

{
final var found = findUsingBindingMethods(signature.getClazz());
if (found != null) {
log.debug("status=foundUsingBindingMethods");
return found;
}
}

{
final var found = findUsingCtx(signature);
if (found != null) {
log.debug("status=foundByUsingCtx");
return found;
}
}
log.debug("status=notFound, class={}", signature);
return null;

// todo procurar a classe que o obj grah impl estende ou a interface que ele implementa
// Pegar a anotação @Component e pegar os modulos
// andar pelos metodos de cada modulo procurando pelo método que retorna o tipo da interface desejada
// e que tenha @Binds , provides nao serve como ele pode receber um tipo pra internamente montar o
// tipo retornado mas daih nao da obter a instancia

}

private Object findUsingCtx(Signature signature) {
try {
final var method = MethodUtils
.getAllMethods(this.getCtxClass())
.stream()
.filter(it -> isAssignable(it, signature))
.findFirst();
if (method.isPresent()) {
return MethodUtils.invoke(method.get(), this.ctx, true);
}
} catch (Throwable e) {
log.warn("status=failedToFindByProvider, msg={}", e.getMessage());
log.warn("status=failedToFindByMethodOnCtx, msg={}", e.getMessage());
}
return null;
}

private static boolean isAssignable(Method m, Signature sig) {
final var mSig = Signature.ofMethodReturnType(m);
final var assignable = mSig.isSameOrInheritFrom(sig) && m.getParameterTypes().length == 0;
log.trace("status=comparing, assignable={}, mSig={}, sig={}", assignable, mSig, sig);
return assignable;
}

// find by binding methods
private Object findUsingBindingMethods(Class<?> clazz) {
try {
final var bindingMethod = BindingMethod.findBindingMethod(this);
if (bindingMethod == null) {
Expand All @@ -44,29 +95,21 @@ public Object get(Class<?> clazz) {
} catch (Throwable e) {
log.warn("status=failedToFindByBinding, msg={}", e.getMessage());
}
return null;
}

// find by ctx obj methods
private Object findUsingProvider(Class<?> clazz) {
try {
final var method = MethodUtils
.getAllMethods(this.getCtxClass())
.stream()
.filter(it -> it.getReturnType().isAssignableFrom(clazz) && it.getParameterTypes().length == 0)
.findFirst();
if (method.isPresent()) {
return MethodUtils.invoke(method.get(), this.ctx, true);
final var provider = this.findProviderFor(clazz);
if (provider != null) {
log.debug("status=beanSolved, from=Provider, beanClass={}", clazz);
return provider.getValue();
}
return null;
} catch (Throwable e) {
log.warn("status=failedToFindByMethodOnCtx, msg={}", e.getMessage());
log.warn("status=failedToFindByProvider, msg={}", e.getMessage());
return null;
}

return null;

// todo procurar a classe que o obj grah impl estende ou a interface que ele implementa
// Pegar a anotação @Component e pegar os modulos
// andar pelos metodos de cada modulo procurando pelo método que retorna o tipo da interface desejada
// e que tenha @Binds , provides nao serve como ele pode receber um tipo pra internamente montar o
// tipo retornado mas daih nao da obter a instancia

}

public Object getCtx() {
Expand Down Expand Up @@ -126,4 +169,5 @@ static Field findFirstProviderFieldWithType(final Class<?> clazz, Class<?> wante
public Class<?> getCtxClass() {
return this.ctx.getClass();
}

}
22 changes: 17 additions & 5 deletions src/test/java/dagger/sheath/junit/DaggerExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import dagger.sheath.InjectSpy;
import dagger.sheath.NopSupplier;
import dagger.sheath.ProviderWrapper;
import dagger.sheath.reflection.Signature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.junit.jupiter.api.extension.AfterAllCallback;
Expand Down Expand Up @@ -64,6 +66,7 @@ public void afterAll(ExtensionContext context) throws Exception {

@Override
public void beforeEach(ExtensionContext context) throws Exception {
log.debug("status=beforeEach");
injectMocksAndSpies(context);
injectFields(context);
resetMocks(context);
Expand Down Expand Up @@ -143,11 +146,11 @@ private static void inject(
try {
log.debug("status=injectMockSpyInField, field={} {}", field.getType().getSimpleName(), field.getName());
ctxWrapper.initializeWithOrThrows(field.getType(), initializer);
final var mock = ctxWrapper.get(field.getType());
final var mock = ctxWrapper.get(Signature.of(field));
if (!validator.test(mock)) {
throw new IllegalStateException(String.format("Mock/Stub didn't work for type: %s", field.getType()));
}
FieldUtils.writeField(field, instance, mock, true);
writeTo(instance, field, mock);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
Expand Down Expand Up @@ -179,14 +182,23 @@ <T extends Annotation> Optional<T> findAnnotation(final ExtensionContext context
static void injectFields(ExtensionContext context) throws IllegalAccessException {
final var ctx = findCtxWrapper(context);
final var testInstances = context.getRequiredTestInstances().getAllInstances();
for (Object instance : testInstances) {
final var fields = FieldUtils.getFieldsListWithAnnotation(instance.getClass(), Inject.class);
for (Object testInstance : testInstances) {
final var fields = FieldUtils.getFieldsListWithAnnotation(testInstance.getClass(), Inject.class);
for (Field field : fields) {
FieldUtils.writeField(field, instance, ctx.get(field.getType()), true);
final var foundInstance = ctx.get(Signature.of(field));
writeTo(testInstance, field, foundInstance);
}
}
}

private static void writeTo(Object testInstance, Field field, Object foundInstance) throws IllegalAccessException {
FieldUtils.writeField(field, testInstance, foundInstance, true);
log.debug(
"status=written, testClass={}, field={}, value={}, classToFind={}, generic={}",
field.getName(), ClassUtils.getSimpleName(testInstance), foundInstance, field.getType(), field.getGenericType()
);
}

static CtxWrapper findCtxWrapper(ExtensionContext context) {
return context.getStore(DAGGER).get(DAGGER_CTX_WRAPPER, CtxWrapper.class);
}
Expand Down
89 changes: 89 additions & 0 deletions src/test/java/dagger/sheath/reflection/Signature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package dagger.sheath.reflection;

import com.google.common.reflect.TypeToken;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.apache.commons.lang3.ObjectUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

@Value
@Builder
@EqualsAndHashCode(of = "typeArguments")
public class Signature {

private Class<?> clazz;
private Type[] typeArguments;


public boolean isSameOrInheritFrom(Signature sig) {
return this.clazz.isAssignableFrom(sig.getClazz()) && this.areTypeArgumentsSameOrInheritFrom(sig);
}

boolean areTypeArgumentsSameOrInheritFrom(Signature sig) {
if (ObjectUtils.allNull(this.typeArguments, sig.typeArguments)) {
return true;
}
if (this.typeArguments == null && sig.typeArguments != null) {
return true;
}
if (sig.typeArguments == null) {
return false;
}
if (this.typeArguments.length != sig.typeArguments.length) {
return false;
}
for (int i = 0; i < this.typeArguments.length; i++) {
if (!this.isTypeArgumentSameOrInheritFrom(sig, i)) {
return false;
}
}
return true;
}

private boolean isTypeArgumentSameOrInheritFrom(Signature sig, int i) {
final var type = TypeToken.of(this.typeArguments[i]).getRawType();
final var otherType = TypeToken.of(sig.typeArguments[i]).getRawType();
return type.isAssignableFrom(otherType);
}

public String getFirstTypeArgumentName() {
if (this.typeArguments != null && this.typeArguments.length > 0) {
return this.typeArguments[0].getTypeName();
}
return null;
}

public static Signature of(Field f) {
return Signature
.builder()
.clazz(f.getType())
.typeArguments(findTypeArguments(f.getGenericType()))
.build();
}

public static Signature of(Type type) {
return Signature
.builder()
.clazz(TypeToken.of(type).getRawType())
.typeArguments(findTypeArguments(type))
.build();
}

public static Signature ofMethodReturnType(Method m) {
return of(m.getGenericReturnType());
}

private static Type[] findTypeArguments(Type type) {
if (type instanceof ParameterizedType) {
return ((ParameterizedType) type).getActualTypeArguments();
}
return null;
}


}
Loading
Loading