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

add support for generic component classes #629

Open
wants to merge 1 commit into
base: develop
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.artemis.generator.strategy.e;

import static com.artemis.generator.util.ExtendedTypeReflection.resolveGenericType;
import static com.artemis.generator.util.ExtendedTypeReflection.resolveGenericReturnType;
import com.artemis.annotations.Fluid;
import com.artemis.annotations.FluidMethod;
import com.artemis.generator.common.IterativeModelStrategy;
Expand All @@ -10,6 +12,7 @@
import com.artemis.generator.util.MethodBuilder;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;

/**
Expand Down Expand Up @@ -64,7 +67,7 @@ private boolean isSetter(Method method) {
private MethodDescriptor methodProxyReturnFluidMethod(ComponentDescriptor component, Method method) {
MethodBuilder builder = new MethodBuilder(FluidTypes.E_TYPE, component.getCompositeName(method.getName()));

String arguments = appendParameters(method, builder);
String arguments = appendParameters(component, method, builder);

return builder
.mapper(component, ".create(entityId)." + method.getName() + "(" + arguments + ")")
Expand All @@ -73,22 +76,22 @@ private MethodDescriptor methodProxyReturnFluidMethod(ComponentDescriptor compon
.build();
}

private String appendParameters(Method method, MethodBuilder builder) {
int count = 0;
private String appendParameters(ComponentDescriptor component, Method method, MethodBuilder builder) {
String arguments = "";
for (Class<?> parameterType : method.getParameterTypes()) {
builder.parameter(parameterType, "p" + count);
arguments = arguments + (arguments.isEmpty() ? "" : ", ") + "p" + count;
count++;
final Type[] parameterTypes = method.getGenericParameterTypes();
final int paramCount = parameterTypes.length;
for(int i = 0; i < paramCount; i++) {
builder.parameter(resolveGenericType(component, method, parameterTypes[i]), "p" + i);
arguments = arguments + (arguments.isEmpty() ? "" : ", ") + "p" + i;
}
return arguments;
}

private MethodDescriptor methodProxyMethod(ComponentDescriptor component, Method method) {

MethodBuilder builder = new MethodBuilder(method.getGenericReturnType(), component.getCompositeName(method.getName()));
MethodBuilder builder = new MethodBuilder(resolveGenericReturnType(component, method), component.getCompositeName(method.getName()));

String arguments = appendParameters(method, builder);
String arguments = appendParameters(component, method, builder);

return builder
.mapper("return ", component, ".create(entityId)." + method.getName() + "(" + arguments + ")")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.artemis.generator.strategy.e;

import static com.artemis.generator.util.ExtendedTypeReflection.resolveGenericType;
import com.artemis.generator.model.FluidTypes;
import com.artemis.generator.model.artemis.ComponentDescriptor;
import com.artemis.generator.model.type.MethodDescriptor;
Expand Down Expand Up @@ -46,7 +47,7 @@ public void execute(ComponentDescriptor component, Field field, TypeModel model)
* int E::posX() -> obtain field directly via interface.
*/
private MethodDescriptor fieldGetterMethod(ComponentDescriptor component, Field field) {
return new MethodBuilder(field.getGenericType(), component.getCompositeName(field.getName()))
return new MethodBuilder(resolveGenericType(component, field), component.getCompositeName(field.getName()))
.mapper("return ", component, ".create(entityId)." + field.getName())
.debugNotes(field.toGenericString())
.build();
Expand All @@ -58,7 +59,7 @@ private MethodDescriptor fieldGetterMethod(ComponentDescriptor component, Field
private MethodDescriptor fieldSetterMethod(ComponentDescriptor component, Field field) {
final String parameterName = field.getName();
return new MethodBuilder(FluidTypes.E_TYPE, component.getCompositeName(parameterName))
.parameter(field.getGenericType(), parameterName)
.parameter(resolveGenericType(component, field), parameterName)
.mapper(component, ".create(this.entityId)." + parameterName + "=" + parameterName)
.debugNotes(field.toGenericString())
.returnFluid()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.artemis.generator.util;

import com.artemis.generator.model.artemis.ComponentDescriptor;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import org.reflections.ReflectionUtils;

import java.lang.annotation.Annotation;
Expand Down Expand Up @@ -103,4 +105,53 @@ public boolean apply(T input) {
};
}

/**
* Resolve the actual type of the provided field.
* For non-generic fields, this will just return the field type.
* For generic fields, try to determine the provided generic type arguments and deduce the real type from it.
*/
public static Type resolveGenericType(final ComponentDescriptor component, final Field field) {
return resolveGenericType(component.getComponentType(), field.getDeclaringClass(), field.getGenericType());
}

/**
* Resolve the actual type of the methods parameter.
* For non-generic methods, this will just return the parameters type.
* For generic methods, try to determine the provided generic type arguments and deduce the real type from it.
*/
public static Type resolveGenericType(final ComponentDescriptor component, final Method method, final Type type) {
return resolveGenericType(component.getComponentType(), method.getDeclaringClass(), type);
}

/**
* Resolve the actual return type of the method.
* For non-generic methods, this will just return the parameters type.
* For generic methods, try to determine the provided generic type arguments and deduce the real return type from it.
*/
public static Type resolveGenericReturnType(final ComponentDescriptor component, final Method method) {
return resolveGenericType(component.getComponentType(), method.getDeclaringClass(), method.getGenericReturnType());
}

/**
* Tries to deduce the actual runtime type for the generic type parameter.
* @param componentType the class of a component
* @param declaringType a base class of the given component class
* @param typeParam the generic type parameter that needs to be resolved
* @return the actual type
*/
@SuppressWarnings({"UnstableApiUsage", "rawtypes", "unchecked"})
private static Type resolveGenericType(final Class<?> componentType, final Class<?> declaringType, final Type typeParam) {
if(declaringType.isAssignableFrom(componentType) && typeParam instanceof TypeVariable<?>) {
final TypeToken actual = TypeToken.of(componentType);
final TypeToken declared = actual.getSupertype(declaringType);
final ParameterizedType parameterizedType = (ParameterizedType) declared.getType();
final TypeVariable<?>[] declaredTypes = ((Class<?>) parameterizedType.getRawType()).getTypeParameters();
final Type[] actualTypes = parameterizedType.getActualTypeArguments();
final int typeIndex = Arrays.asList(declaredTypes).indexOf(typeParam);
if (typeIndex >= 0) {
return actualTypes[typeIndex];
}
}
return typeParam;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,20 @@ public void When_public_field_with_parameterized_Type_Should_expose_as_parameter
TypeModel model = applyStrategy(ComponentFieldAccessorStrategy.class, Proof.class);
assertHasMethod(model, "com.artemis.E proofGen(java.util.List<java.lang.Object> gen)");
}

@Test
public void When_public_field_is_defined_in_generic_base_class() {
TypeModel model = applyStrategy(ComponentFieldAccessorStrategy.class, StringComponent.class);
assertHasMethod(model, "com.artemis.E stringComponentValue(java.lang.String value)");
assertHasMethod(model, "java.lang.String stringComponentValue()");
}

@Test
public void When_public_field_is_defined_in_generic_base_class_with_intermediate_class() {
TypeModel model = applyStrategy(ComponentFieldAccessorStrategy.class, SimpleComponent.class);
assertHasMethod(model, "com.artemis.E simpleComponentValue1(java.lang.Integer value1)");
assertHasMethod(model, "com.artemis.E simpleComponentValue2(java.lang.String value2)");
assertHasMethod(model, "java.lang.Integer simpleComponentValue1()");
assertHasMethod(model, "java.lang.String simpleComponentValue2()");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,15 @@ public void When_public_void_parameterized_method_with_own_component_type_as_ret
assertHasMethod(model,"com.artemis.generator.strategy.e.Proof proofFluid(com.artemis.generator.strategy.e.Proof p0)");
}

@Test
public void When_public_void_parameterized_method_is_defined_in_generic_base_class() {
TypeModel model = applyStrategy(ComponentMethodProxyStrategy.class, SimpleComponent.class);
assertHasMethod(model,"com.artemis.E simpleComponent(java.lang.Integer p0,java.lang.String p1,int p2)");
}

@Test
public void When_public_return_value_parameterized_method_is_defined_in_generic_base_class() {
TypeModel model = applyStrategy(ComponentMethodProxyStrategy.class, SimpleComponent.class);
assertHasMethod(model,"java.lang.Integer simpleComponentDummy(java.lang.Integer p0,java.lang.String p1,int p2)");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.artemis.generator.strategy.e;

import com.artemis.Component;

abstract class SingleValueComponent<T> extends Component {
public T value;
}

final class StringComponent extends SingleValueComponent<String> { }

abstract class BaseComponent<T1, T2> extends Component {
public T1 value1;
public T2 value2;

public void set(T1 a, T2 b, int c) {
value1 = a;
value2 = b;
}

public T1 dummy(T1 a, T2 b, int c) {
return value1;
}
}

abstract class SubComponent<T> extends BaseComponent<T, String> { }

final class SimpleComponent extends SubComponent<Integer> { }