Skip to content

Commit

Permalink
- feature: @CosId to support Generic type for ID
Browse files Browse the repository at this point in the history
  • Loading branch information
Ahoo-Wang committed Jan 24, 2022
1 parent 7318730 commit 3b87fbd
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ public boolean ensureId(Object target) {
return ensureId.ensureId(target);
}


public class EnsureStringId implements EnsureId {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,24 @@
*/
public class IdDefinition {

public static final IdDefinition NOT_FOUND = new IdDefinition(null);
public static final IdDefinition NOT_FOUND = new IdDefinition(null, null, null);

private final String generatorName;
private final Field idField;
private final Class<?> idType;

public IdDefinition(Field idField) {
this(IdGeneratorProvider.SHARE, idField);
}

public IdDefinition(String generatorName, Field idField) {
this(generatorName, idField, idField.getType());
}

public IdDefinition(String generatorName, Field idField, Class<?> idType) {
this.generatorName = generatorName;
this.idField = idField;
this.idType = idType;
}

public String getGeneratorName() {
Expand All @@ -43,4 +49,8 @@ public String getGeneratorName() {
public Field getIdField() {
return idField;
}

public Class<?> getIdType() {
return idType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ default Class<?> getIdDeclaringClass() {
}

default Class<?> getIdType() {
return getIdField().getType();
return getIdDefinition().getIdType();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,17 @@
import me.ahoo.cosid.accessor.method.MethodGetter;
import me.ahoo.cosid.accessor.method.MethodSetter;

import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
Expand Down Expand Up @@ -94,10 +100,13 @@ public static Method parseSetter(Field field) {
protected CosIdAccessor parseClass(Class<?> clazz) {
CosIdAccessor firstAccessor = CosIdAccessor.NOT_FOUND;
Class<?> currentDeclaringClass = clazz;
List<Class<?>> lookupClassList = new ArrayList<>();
while (!Object.class.equals(currentDeclaringClass)) {
lookupClassList.add(currentDeclaringClass);

for (Field declaredField : currentDeclaringClass.getDeclaredFields()) {

IdDefinition idDefinition = definitionParser.parse(clazz, declaredField);
IdDefinition idDefinition = definitionParser.parse(lookupClassList, declaredField);
if (idDefinition == null
|| IdDefinition.NOT_FOUND.equals(idDefinition)) {
continue;
Expand All @@ -106,19 +115,52 @@ protected CosIdAccessor parseClass(Class<?> clazz) {
if (!CosIdAccessor.NOT_FOUND.equals(firstAccessor)) {
throw new MultipleIdNotSupportException(clazz);
}

firstAccessor = definitionAsAccessor(idDefinition);

IdDefinition fixedIdDefinition = fixGenericFieldActualType(lookupClassList, idDefinition);
firstAccessor = definitionAsAccessor(fixedIdDefinition);
}

currentDeclaringClass = currentDeclaringClass.getSuperclass();
}
return firstAccessor;
}

private IdDefinition fixGenericFieldActualType(List<Class<?>> lookupClassList, IdDefinition idDefinition) {
Type fieldGenericType = idDefinition.getIdField().getGenericType();
if (!(fieldGenericType instanceof TypeVariable)) {
return idDefinition;
}

for (int i = lookupClassList.size() - 2; i >= 0; i--) {
Class<?> superClass = lookupClassList.get(i + 1);
Class<?> subClass = lookupClassList.get(i);
fieldGenericType = getActualFieldType(fieldGenericType, superClass, subClass);
if (fieldGenericType instanceof Class<?>) {
return new IdDefinition(idDefinition.getGeneratorName(), idDefinition.getIdField(), (Class<?>) fieldGenericType);
}
}
return idDefinition;
}

private Type getActualFieldType(Type typeVariable, Class<?> superClass, Class<?> subClass) {
int genericVarIdx = -1;
TypeVariable<?>[] typeVariables = superClass.getTypeParameters();
for (int i = 0; i < typeVariables.length; i++) {
if (typeVariable.equals(typeVariables[i])) {
genericVarIdx = i;
}
}
if (genericVarIdx == -1) {
throw new IllegalArgumentException(Strings.lenientFormat("Type Parameter:[%s] not found in Class:[%].", typeVariable, superClass));
}

ParameterizedType genericSuperclass = (ParameterizedType) subClass.getGenericSuperclass();
Type[] actualTypes = genericSuperclass.getActualTypeArguments();
return actualTypes[genericVarIdx];
}

protected CosIdAccessor definitionAsAccessor(IdDefinition idDefinition) {
Field idField = idDefinition.getIdField();
if (!CosIdAccessor.availableType(idField.getType())) {
if (!CosIdAccessor.availableType(idDefinition.getIdType())) {
throw new IdTypeNotSupportException(idField);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
import me.ahoo.cosid.accessor.IdDefinition;

import java.lang.reflect.Field;
import java.util.List;

/**
* @author ahoo wang
*/
@FunctionalInterface
public interface FieldDefinitionParser {
IdDefinition parse(Class<?> clazz, Field field);
IdDefinition parse(List<Class<?>> lookupClassList, Field field);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import me.ahoo.cosid.accessor.IdDefinition;

import java.lang.reflect.Field;
import java.util.List;

/**
* @author ahoo wang
Expand All @@ -29,7 +30,7 @@ public NamedDefinitionParser(String idFieldName) {
}

@Override
public IdDefinition parse(Class<?> clazz, Field field) {
public IdDefinition parse(List<Class<?>> lookupClassList, Field field) {
if (!idFieldName.equals(field.getName())) {
return IdDefinition.NOT_FOUND;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;

/**
* @author ahoo wang
Expand All @@ -29,12 +31,16 @@ public class AnnotationDefinitionParser implements FieldDefinitionParser {
public static final AnnotationDefinitionParser INSTANCE = new AnnotationDefinitionParser();

@Override
public IdDefinition parse(Class<?> clazz, Field field) {
public IdDefinition parse(List<Class<?>> lookupClassList, Field field) {

CosId clazzCosId = clazz.getAnnotation(CosId.class) != null
? clazz.getAnnotation(CosId.class) : field.getDeclaringClass().getAnnotation(CosId.class);
Optional<CosId> clazzCosIdOp = lookupClassList
.stream()
.filter(clazz -> clazz.isAnnotationPresent(CosId.class))
.map(clazz -> clazz.getAnnotation(CosId.class))
.findFirst();

if (null != clazzCosId) {
if (clazzCosIdOp.isPresent()) {
CosId clazzCosId = clazzCosIdOp.get();
if (!field.getName().equals(clazzCosId.field())) {
return IdDefinition.NOT_FOUND;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
*/
class DefaultAccessorParserTest {


public static final DefaultAccessorParser ACCESSOR_PARSER = new DefaultAccessorParser(AnnotationDefinitionParser.INSTANCE);

@SneakyThrows
Expand Down Expand Up @@ -168,6 +167,30 @@ void classCosIdInherited() {
Assertions.assertEquals(AbstractCosId.class.getDeclaredField("id"), cosIdAccessor.getIdField());
}


@SneakyThrows
@Test
void abstractGenericCosId() {
Assertions.assertThrows(IdTypeNotSupportException.class, () -> {
ACCESSOR_PARSER.parse(AbstractGenericCosId.class);
});
}

@SneakyThrows
@Test
void classIdGenericInherited() {
DefaultCosIdAccessor cosIdAccessor = (DefaultCosIdAccessor) ACCESSOR_PARSER.parse(ClassIdGenericInheritedType.class);
Assertions.assertEquals(AbstractGenericCosId.class.getDeclaredField("id"), cosIdAccessor.getIdField());
}

@SneakyThrows
@Test
void classIdGenericFourInherited() {
DefaultCosIdAccessor cosIdAccessor = (DefaultCosIdAccessor) ACCESSOR_PARSER.parse(ClassIdGenericFourInheritedType.class);
Assertions.assertEquals(AbstractGenericCosId.class.getDeclaredField("id"), cosIdAccessor.getIdField());
}


@Test
void capitalize() {
}
Expand Down Expand Up @@ -331,4 +354,45 @@ public void setId(long id) {
public static class ClassCosIdInheritedType extends AbstractCosId {

}

@CosId(field = "id")
public abstract static class AbstractGenericCosId<T, N> {
private T id;
private N name;

public T getId() {
return id;
}

public void setId(T id) {
this.id = id;
}

public N getName() {
return name;
}

public void setName(N name) {
this.name = name;
}
}


public static class ClassIdGenericInheritedType extends AbstractGenericCosId<Long, String> {

}

public static class ClassIdGenericTwoInheritedType<T, O> extends AbstractGenericCosId<O, T> {

}


public static class ClassIdGenericThreeInheritedType<H> extends ClassIdGenericTwoInheritedType<String, H> {

}

public static class ClassIdGenericFourInheritedType extends ClassIdGenericThreeInheritedType<Long> {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.util.Arrays;

/**
* @author ahoo wang
Expand All @@ -34,7 +35,7 @@ class NamedAccessorParserTest {
@Test
void parse() {
Field idField = LongIdEntity.class.getDeclaredField("id");
IdDefinition idDefinition = NAMED_PARSER.parse(LongIdEntity.class, idField);
IdDefinition idDefinition = NAMED_PARSER.parse(Arrays.asList(LongIdEntity.class), idField);
Assertions.assertNotEquals(IdDefinition.NOT_FOUND, idDefinition);
Assertions.assertEquals(IdGeneratorProvider.SHARE, idDefinition.getGeneratorName());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.util.Arrays;

/**
* @author ahoo wang
Expand All @@ -32,7 +33,7 @@ public class CosIdAnnotationAccessorParserTest {
@Test
void parse() {
Field idField = LongIdEntity.class.getDeclaredField("id");
IdDefinition idDefinition = AnnotationDefinitionParser.INSTANCE.parse(LongIdEntity.class, idField);
IdDefinition idDefinition = AnnotationDefinitionParser.INSTANCE.parse(Arrays.asList(LongIdEntity.class), idField);

Assertions.assertNotEquals(IdDefinition.NOT_FOUND, idDefinition);
Assertions.assertEquals(IdGeneratorProvider.SHARE, idDefinition.getGeneratorName());
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#

group=me.ahoo.cosid
version=1.8.2
version=1.8.3

description=Global distributed ID generator
website=https://github.com/Ahoo-Wang/CosId
Expand Down

0 comments on commit 3b87fbd

Please sign in to comment.