- 适配对象循环嵌套

- 优化转换逻辑
This commit is contained in:
linpeilie 2024-03-12 10:39:27 +08:00
parent 19f163d174
commit 4e103c7cd1
25 changed files with 632 additions and 307 deletions

View File

@ -0,0 +1,19 @@
package io.github.linpeilie.model;
import io.github.linpeilie.annotations.AutoMapper;
import java.util.Set;
import lombok.Data;
@Data
@AutoMapper(target = DeptDTO.class)
public class Dept {
private String code;
private String name;
private Long parentId;
private Set<Dept> children;
}

View File

@ -0,0 +1,17 @@
package io.github.linpeilie.model;
import io.github.linpeilie.annotations.AutoMapper;
import java.util.Set;
import lombok.Data;
@Data
@AutoMapper(target = Dept.class)
public class DeptDTO {
private Long id;
private String name;
private Set<DeptDTO> children;
}

View File

@ -0,0 +1,12 @@
package io.github.linpeilie.model;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
@Data
@AutoMapper(target = Employee1Dto.class)
public class Employee1 {
private Employee employee;
}

View File

@ -0,0 +1,12 @@
package io.github.linpeilie.model;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
@Data
@AutoMapper(target = Employee1.class)
public class Employee1Dto {
private EmployeeDto employee;
}

View File

@ -1,10 +1,12 @@
package io.github.linpeilie.model;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@AutoMapper(target = ProductPropertyDto.class, cycles = true)
public class ProductProperty {
private Long id;

View File

@ -1,10 +1,12 @@
package io.github.linpeilie.model;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@AutoMapper(target = ProductProperty.class, cycles = true)
public class ProductPropertyDto {
private Long id;

View File

@ -9,38 +9,78 @@ import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import io.github.linpeilie.processor.metadata.AbstractAdapterMethodMetadata;
import io.github.linpeilie.processor.metadata.AdapterMapMethodMetadata;
import io.github.linpeilie.processor.metadata.AdapterMethodMetadata;
import io.github.linpeilie.processor.utils.ClassUtil;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import static javax.tools.Diagnostic.Kind.ERROR;
public abstract class AbstractAdapterMapperGenerator {
protected static final String PARAM__PARAMETER_NAME = "param";
protected static final String CONTEXT__PARAMETER_NAME = "context";
public void write(ProcessingEnvironment processingEnv,
Collection<AbstractAdapterMethodMetadata> adapterMethods,
String adapterClassName) {
String adapterClassName,
boolean cycle) {
write(processingEnv, createAdapterTypeSpec(adapterClassName, adapterMethods, cycle));
}
private void write(ProcessingEnvironment processingEnv, TypeSpec typeSpec) {
if (typeSpec == null) {
return;
}
// write Adapter
try (final Writer writer = processingEnv.getFiler()
.createSourceFile(adapterPackage() + "." + adapterClassName)
.createSourceFile(adapterPackage() + "." + typeSpec.name)
.openWriter()) {
JavaFile.builder(adapterPackage(), createTypeSpec(adapterMethods, adapterClassName))
JavaFile.builder(adapterPackage(), typeSpec)
.build()
.writeTo(writer);
} catch (IOException e) {
processingEnv.getMessager()
.printMessage(ERROR, "Error while opening " + adapterClassName + " output file: " + e.getMessage());
.printMessage(ERROR, "Error while opening " + typeSpec.name + " output file: " + e.getMessage());
}
}
protected abstract TypeSpec createTypeSpec(Collection<AbstractAdapterMethodMetadata> adapterMethods,
String adapterClassName);
protected TypeSpec createAdapterTypeSpec(String adapterClassName,
Collection<AbstractAdapterMethodMetadata> adapterMethods,
boolean cycle) {
List<MethodSpec> methods = adapterMethods.stream()
.filter(method -> !cycle || method.needCycleAvoiding())
.map(method -> buildProxyMethod(method, cycle))
.flatMap(Collection::stream)
.collect(Collectors.toList());
if (methods.isEmpty()) {
return null;
}
return createTypeSpec(methods, adapterClassName,
cycle ? ClassName.get(adapterPackage(), AutoMapperProperties.getAdapterClassName()) : null);
}
protected TypeSpec createTypeSpec(List<MethodSpec> methods, String adapterClassName, ClassName superClass) {
TypeSpec.Builder adapterBuilder = TypeSpec.classBuilder(ClassName.get(adapterPackage(), adapterClassName))
.addModifiers(Modifier.PUBLIC);
if (superClass != null) {
adapterBuilder.superclass(superClass);
}
// adapter methods
adapterBuilder.addMethods(methods);
return adapterBuilder.build();
}
protected String adapterPackage() {
return AutoMapperProperties.getAdapterPackage();
@ -57,50 +97,138 @@ public abstract class AbstractAdapterMapperGenerator {
return source;
}
protected List<MethodSpec> buildProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) {
private List<MethodSpec> buildProxyMethod(AbstractAdapterMethodMetadata adapterMethod, boolean cycle) {
List<MethodSpec> methodSpecs = new ArrayList<>();
if (cycle) {
methodSpecs.addAll(buildCycleAvoidingProxyMethod(adapterMethod));
} else {
methodSpecs.addAll(buildDefaultProxyMethod(adapterMethod, null));
}
return methodSpecs;
}
protected List<MethodSpec> buildDefaultProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata,
ClassName annotation) {
List<MethodSpec> methodSpecs = new ArrayList<>();
if (adapterMethodMetadata.needCycleAvoiding()) {
methodSpecs.add(buildCycleAvoidingProxyMethod(adapterMethodMetadata));
} else {
methodSpecs.add(buildDefaultProxyMethod(adapterMethodMetadata));
ParameterSpec parameterSpec =
ParameterSpec.builder(wrapperTypeName(adapterMethodMetadata.getSource()), PARAM__PARAMETER_NAME).build();
MethodSpec methodSpecForSingle =
buildDefaultProxyMethod(adapterMethodMetadata, parameterSpec, adapterMethodMetadata.getReturn(),
annotation);
methodSpecs.add(methodSpecForSingle);
// 自定义类型转换
if (adapterMethodMetadata instanceof AdapterMethodMetadata) {
ParameterSpec listParameter = ParameterSpec.builder(
ParameterizedTypeName.get(ClassName.get("java.util", "List"), adapterMethodMetadata.getSource()),
PARAM__PARAMETER_NAME
).build();
MethodSpec methodSpecForList =
buildDefaultProxyMethod(adapterMethodMetadata, listParameter, ClassName.get("java.util", "List"),
annotation);
methodSpecs.add(methodSpecForList);
}
// 自定义类型与 Map 进行转换
else if (adapterMethodMetadata instanceof AdapterMapMethodMetadata) {
methodSpecs.add(buildObjConversionProxyMethod(adapterMethodMetadata, annotation));
}
return methodSpecs;
}
protected MethodSpec buildDefaultProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) {
private MethodSpec buildObjConversionProxyMethod(AbstractAdapterMethodMetadata adapterMethod,
ClassName annotation) {
CodeBlock code = CodeBlock.builder()
.add("if($N == null) {\n", PARAM__PARAMETER_NAME)
.add(" return null;\n")
.add("}\n")
.add("if($N instanceof $T) {\n", PARAM__PARAMETER_NAME, ClassName.get("java.util", "Map"))
.add(" return $N((Map<$T, $T>) $N);\n",
adapterMethod.getMethodName(), ClassName.get("java.lang", "String"),
ClassName.get("java.lang", "Object"), PARAM__PARAMETER_NAME)
.add("}\n")
.add("return null;\n")
.build();
MethodSpec.Builder methodSpecBuilder =
MethodSpec.methodBuilder("objTo" + ClassUtil.simplifyQualifiedName(adapterMethod.getReturn().toString()))
.addModifiers(Modifier.PUBLIC)
.addParameter(
ParameterSpec.builder(ClassName.get("java.lang", "Object"), PARAM__PARAMETER_NAME)
.build()
)
.returns(adapterMethod.getReturn())
.addCode(code);
if (annotation != null) {
methodSpecBuilder.addAnnotation(annotation);
}
return methodSpecBuilder.build();
}
private MethodSpec buildDefaultProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata,
ParameterSpec parameterSpec,
TypeName returns,
ClassName annotation) {
CodeBlock targetCode = adapterMethodMetadata.isStatic()
? CodeBlock.of("return $T.$N($N);", adapterMethodMetadata.getMapper(),
adapterMethodMetadata.getMapperMethodName(), "param")
: proxyMethodTarget(adapterMethodMetadata);
ParameterSpec parameterSpec =
ParameterSpec.builder(wrapperTypeName(adapterMethodMetadata.getSource()), "param").build();
return MethodSpec.methodBuilder(adapterMethodMetadata.getMethodName())
MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(adapterMethodMetadata.getMethodName())
.addModifiers(Modifier.PUBLIC)
.addParameter(parameterSpec)
.returns(adapterMethodMetadata.getReturn())
.addCode(targetCode)
.build();
.returns(returns)
.addCode(targetCode);
if (annotation != null) {
methodSpecBuilder.addAnnotation(annotation);
}
return methodSpecBuilder.build();
}
protected MethodSpec buildCycleAvoidingProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) {
protected List<MethodSpec> buildCycleAvoidingProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) {
// default impl
List<MethodSpec> defaultMethods = buildDefaultProxyMethod(adapterMethodMetadata,
ClassName.get("io.github.linpeilie.annotations", "DoIgnore"));
List<MethodSpec> methodSpecs = new ArrayList<>(defaultMethods);
ParameterSpec parameterSpec =
ParameterSpec.builder(wrapperTypeName(adapterMethodMetadata.getSource()), PARAM__PARAMETER_NAME).build();
methodSpecs.add(
buildCycleAvoidingProxyMethod(adapterMethodMetadata, parameterSpec, adapterMethodMetadata.getReturn()));
if (adapterMethodMetadata instanceof AdapterMethodMetadata) {
ParameterSpec listParameter = ParameterSpec.builder(
ParameterizedTypeName.get(ClassName.get("java.util", "List"), adapterMethodMetadata.getSource()),
"param"
).build();
MethodSpec methodSpecForList =
buildCycleAvoidingProxyMethod(adapterMethodMetadata, listParameter,
ClassName.get("java.util", "List"));
methodSpecs.add(methodSpecForList);
}
return methodSpecs;
}
private MethodSpec buildCycleAvoidingProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata,
ParameterSpec parameterSpec,
TypeName returns
) {
CodeBlock targetCode = adapterMethodMetadata.isStatic()
? CodeBlock.of("return $T.$N($N, $N);", adapterMethodMetadata.getMapper(),
adapterMethodMetadata.getMapperMethodName(), "param", "context")
adapterMethodMetadata.getMapperMethodName(), "param", CONTEXT__PARAMETER_NAME)
: cycleAvoidingMethodTarget(adapterMethodMetadata);
ParameterSpec parameterSpec =
ParameterSpec.builder(wrapperTypeName(adapterMethodMetadata.getSource()), "param").build();
ParameterSpec contextParameterSpec =
ParameterSpec.builder(
ClassName.get("io.github.linpeilie", "CycleAvoidingMappingContext"),
"context")
ClassName.get("io.github.linpeilie", "CycleAvoidingMappingContext"),
CONTEXT__PARAMETER_NAME)
.addAnnotation(ClassName.get("org.mapstruct", "Context"))
.build();
return MethodSpec.methodBuilder(adapterMethodMetadata.getMethodName())
.addModifiers(Modifier.PUBLIC)
.addParameter(parameterSpec)
.addParameter(contextParameterSpec)
.returns(adapterMethodMetadata.getReturn())
.returns(returns)
.addCode(targetCode)
.build();
}

View File

@ -36,6 +36,7 @@ import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -46,7 +47,6 @@ import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
@ -63,13 +63,6 @@ import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import org.mapstruct.MappingConstants;
import static io.github.linpeilie.processor.Constants.AUTO_ENUM_MAPPER_ANNOTATION;
import static io.github.linpeilie.processor.Constants.AUTO_MAPPERS_ANNOTATION;
import static io.github.linpeilie.processor.Constants.AUTO_MAPPER_ANNOTATION;
import static io.github.linpeilie.processor.Constants.AUTO_MAP_MAPPER_ANNOTATION;
import static io.github.linpeilie.processor.Constants.COMPONENT_MODEL_CONFIG_ANNOTATION;
import static io.github.linpeilie.processor.Constants.MAPPER_ANNOTATION;
import static io.github.linpeilie.processor.Constants.MAPPER_CONFIG_ANNOTATION;
import static io.github.linpeilie.processor.ProcessorOptions.ADAPTER_CLASS_NAME;
import static io.github.linpeilie.processor.ProcessorOptions.ADAPTER_PACKAGE;
import static io.github.linpeilie.processor.ProcessorOptions.BUILDER_BUILD_METHOD;
@ -83,22 +76,17 @@ import static io.github.linpeilie.processor.ProcessorOptions.UNMAPPED_SOURCE_POL
import static io.github.linpeilie.processor.ProcessorOptions.UNMAPPED_TARGET_POLICY;
import static javax.tools.Diagnostic.Kind.ERROR;
@SupportedAnnotationTypes({AUTO_MAPPER_ANNOTATION, AUTO_MAPPERS_ANNOTATION, AUTO_MAP_MAPPER_ANNOTATION,
AUTO_ENUM_MAPPER_ANNOTATION, MAPPER_CONFIG_ANNOTATION, COMPONENT_MODEL_CONFIG_ANNOTATION,
MAPPER_ANNOTATION})
@SupportedOptions({
MAPPER_CONFIG_CLASS,
MAPPER_PACKAGE,
UNMAPPED_SOURCE_POLICY,
UNMAPPED_TARGET_POLICY,
NULL_VALUE_MAPPING_STRATEGY,
NULL_VALUE_PROPERTY_MAPPING_STRATEGY,
BUILDER_BUILD_METHOD,
BUILDER_DISABLE_BUILDER,
ADAPTER_PACKAGE,
ADAPTER_CLASS_NAME,
MAP_ADAPTER_CLASS_NAME,
})
@SupportedAnnotationTypes({
ContextConstants.Annotations.autoMapper,
ContextConstants.Annotations.autoMappers,
ContextConstants.Annotations.autoMapMapper,
ContextConstants.Annotations.autoEnumMapper,
ContextConstants.Annotations.mapperConfig,
ContextConstants.Annotations.componentModel,
ContextConstants.Annotations.mapper})
@SupportedOptions({MAPPER_CONFIG_CLASS, MAPPER_PACKAGE, UNMAPPED_SOURCE_POLICY, UNMAPPED_TARGET_POLICY,
NULL_VALUE_MAPPING_STRATEGY, NULL_VALUE_PROPERTY_MAPPING_STRATEGY, BUILDER_BUILD_METHOD,
BUILDER_DISABLE_BUILDER, ADAPTER_PACKAGE, ADAPTER_CLASS_NAME, MAP_ADAPTER_CLASS_NAME,})
public class AutoMapperProcessor extends AbstractProcessor {
private static final ClassName MAPPING_DEFAULT_TARGET = ClassName.get("io.github.linpeilie", "DefaultMapping");
@ -121,46 +109,39 @@ public class AutoMapperProcessor extends AbstractProcessor {
private Messager messager;
private Filer filer;
public AutoMapperProcessor() {
this.mapperGenerator = new AutoMapperGenerator();
this.mapperConfigGenerator = new MapperConfigGenerator();
}
private boolean isAutoMapperAnnotation(TypeElement annotation) {
return AUTO_MAPPER_ANNOTATION.contentEquals(annotation.getQualifiedName());
return ContextConstants.Annotations.autoMapper.contentEquals(annotation.getQualifiedName());
}
private boolean isAutoMappersAnnotation(TypeElement annotation) {
return AUTO_MAPPERS_ANNOTATION.contentEquals(annotation.getQualifiedName());
return ContextConstants.Annotations.autoMappers.contentEquals(annotation.getQualifiedName());
}
private boolean isAutoMapMapperAnnotation(TypeElement annotation) {
return AUTO_MAP_MAPPER_ANNOTATION.contentEquals(annotation.getQualifiedName());
return ContextConstants.Annotations.autoMapMapper.contentEquals(annotation.getQualifiedName());
}
private boolean isAutoEnumMapperAnnotation(TypeElement annotation) {
return AUTO_ENUM_MAPPER_ANNOTATION.contentEquals(annotation.getQualifiedName());
return ContextConstants.Annotations.autoEnumMapper.contentEquals(annotation.getQualifiedName());
}
private boolean isMapperConfigAnnotation(TypeElement annotation) {
return MAPPER_CONFIG_ANNOTATION.contentEquals(annotation.getQualifiedName());
}
private boolean isMapperAnnotation(TypeElement annotation) {
return MAPPER_ANNOTATION.contentEquals(annotation.getQualifiedName());
return ContextConstants.Annotations.mapperConfig.contentEquals(annotation.getQualifiedName());
}
private boolean isComponentModelConfigAnnotation(TypeElement annotation) {
return COMPONENT_MODEL_CONFIG_ANNOTATION.contentEquals(annotation.getQualifiedName());
return ContextConstants.Annotations.componentModel.contentEquals(annotation.getQualifiedName());
}
@Override
public synchronized void init(final ProcessingEnvironment processingEnv) {
super.init(processingEnv);
messager = processingEnv.getMessager();
filer = processingEnv.getFiler();
}
@Override
@ -200,25 +181,27 @@ public class AutoMapperProcessor extends AbstractProcessor {
// AutoMapMapper
final TypeElement autoMapMapperAnnotation =
processingEnv.getElementUtils().getTypeElement(AUTO_MAP_MAPPER_ANNOTATION);
processingEnv.getElementUtils().getTypeElement(ContextConstants.Annotations.autoMapMapper);
processAutoMapMapperAnnotation(roundEnv, autoMapMapperAnnotation);
// AutoEnumMapper
final TypeElement autoEnumMapperAnnotation =
processingEnv.getElementUtils().getTypeElement(AUTO_ENUM_MAPPER_ANNOTATION);
processingEnv.getElementUtils().getTypeElement(ContextConstants.Annotations.autoEnumMapper);
processAutoEnumMapperAnnotation(roundEnv, autoEnumMapperAnnotation);
// AutoMapper
final TypeElement autoMapperAnnotation = processingEnv.getElementUtils().getTypeElement(AUTO_MAPPER_ANNOTATION);
final TypeElement autoMapperAnnotation =
processingEnv.getElementUtils().getTypeElement(ContextConstants.Annotations.autoMapper);
processAutoMapperAnnotation(roundEnv, autoMapperAnnotation);
// AutoMappers
final TypeElement autoMappersAnnotation =
processingEnv.getElementUtils().getTypeElement(AUTO_MAPPERS_ANNOTATION);
processingEnv.getElementUtils().getTypeElement(ContextConstants.Annotations.autoMappers);
processAutoMappersAnnotation(roundEnv, autoMappersAnnotation);
// custom mapper
final TypeElement mapperAnnotation = processingEnv.getElementUtils().getTypeElement(MAPPER_ANNOTATION);
final TypeElement mapperAnnotation =
processingEnv.getElementUtils().getTypeElement(ContextConstants.Mapper.qualifiedClassName);
processMapperAnnotation(roundEnv, mapperAnnotation);
// 生成类
@ -238,7 +221,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
}
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
new BuildCollator(processingEnv, Constants.MAPPERS_FILE_NAME));
new BuildCollator(processingEnv, ContextConstants.MetaInf.mappers));
elements.forEach(element -> customMapperList.add(element.asType()));
}
@ -248,7 +231,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
return;
}
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
new BuildCollator(processingEnv, Constants.ENUM_MAPPERS_FILE_NAME));
new BuildCollator(processingEnv, ContextConstants.MetaInf.enumMappers));
elements.stream()
.map(this::buildAutoEnumMapperMetadata)
.filter(Objects::nonNull)
@ -277,14 +260,12 @@ public class AutoMapperProcessor extends AbstractProcessor {
final AdapterEnumMethodMetadata toValueProxyMethod =
new AdapterEnumMethodMetadata(autoEnumMapperMetadata.getSourceClassName(),
ClassName.get(autoEnumMapperMetadata.mapperPackage(), autoEnumMapperMetadata.mapperName()),
autoEnumMapperMetadata.toValueMethodName(),
autoEnumMapperMetadata.getReturnType());
autoEnumMapperMetadata.toValueMethodName(), autoEnumMapperMetadata.getReturnType());
// toEnum
final AdapterEnumMethodMetadata toEnumProxyMethod =
new AdapterEnumMethodMetadata(autoEnumMapperMetadata.getReturnType(),
ClassName.get(autoEnumMapperMetadata.mapperPackage(), autoEnumMapperMetadata.mapperName()),
autoEnumMapperMetadata.toEnumMethodName(),
autoEnumMapperMetadata.getSourceClassName());
autoEnumMapperMetadata.toEnumMethodName(), autoEnumMapperMetadata.getSourceClassName());
methodMap.putIfAbsent(
autoEnumMapperMetadata.getSourceClassName().simpleName() + toValueProxyMethod.getMapperMethodName(),
toValueProxyMethod);
@ -304,8 +285,8 @@ public class AutoMapperProcessor extends AbstractProcessor {
if (!ElementKind.METHOD.equals(ele.getKind())) {
continue;
}
boolean isGetter = StrUtil.equalsIgnoreCase(ele.getSimpleName(), "get" + enumCodeFieldName)
|| StrUtil.equalsIgnoreCase(ele.getSimpleName(), "is" + enumCodeFieldName);
boolean isGetter = StrUtil.equalsIgnoreCase(ele.getSimpleName(), "get" + enumCodeFieldName) ||
StrUtil.equalsIgnoreCase(ele.getSimpleName(), "is" + enumCodeFieldName);
if (isGetter) {
enumCodeGetterElement = ele;
break;
@ -331,18 +312,20 @@ public class AutoMapperProcessor extends AbstractProcessor {
}
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
new BuildCollator(processingEnv, Constants.AUTO_MAP_MAPPERS_FILE_NAME));
new BuildCollator(processingEnv, ContextConstants.MetaInf.autoMapMappers));
elements.stream()
.map(ele -> buildAutoMapMapperMetadata((TypeElement) ele))
.map(this::buildAutoMapMapperMetadata)
.filter(Objects::nonNull)
.forEach(metadata -> {
this.writeAutoMapperClassFile(metadata);
addAdapterMapMethod(metadata);
});
adapterMapperGenerator.write(processingEnv, mapMethodMap.values(),
AutoMapperProperties.getMapAdapterClassName());
adapterMapperGenerator.write(processingEnv,
mapMethodMap.values(),
AutoMapperProperties.getMapAdapterClassName(),
false);
mapperConfigGenerator.write(processingEnv, AutoMapperProperties.getMapConfigClassName(),
AutoMapperProperties.getMapAdapterClassName(), null);
@ -352,18 +335,20 @@ public class AutoMapperProcessor extends AbstractProcessor {
if (element.getAnnotation(AutoMapMapper.class) == null) {
return null;
}
ClassName source = ClassName.get("java.util", "Map");
ClassName source = ClassName.get(ContextConstants.Map.packageName, ContextConstants.Map.className);
ClassName target = ClassName.get(element);
List<ClassName> uses = Arrays.asList(ClassName.get("io.github.linpeilie.map", "MapObjectConvert"));
List<ClassName> uses = Collections.singletonList(
ClassName.get(ContextConstants.MapObjectConvert.packageName, ContextConstants.MapObjectConvert.className));
final AutoMapperMetadata autoMapperMetadata = new AutoMapMapperMetadata();
autoMapperMetadata.setTargetClassName(target);
autoMapperMetadata.setSourceClassName(source);
autoMapperMetadata.setUsesClassNameList(uses);
autoMapperMetadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseMapMapper"));
autoMapperMetadata.setSuperClass(
ClassName.get(ContextConstants.BaseMapMapper.packageName, ContextConstants.BaseMapMapper.className));
autoMapperMetadata.setSuperGenerics(new ClassName[] {target});
autoMapperMetadata.setMapstructConfigClass(ClassName.get(AutoMapperProperties.getConfigPackage(),
AutoMapperProperties.getMapConfigClassName()));
autoMapperMetadata.setMapstructConfigClass(
ClassName.get(AutoMapperProperties.getConfigPackage(), AutoMapperProperties.getMapConfigClassName()));
return autoMapperMetadata;
}
@ -373,8 +358,6 @@ public class AutoMapperProcessor extends AbstractProcessor {
}
addAdapterMapMethod(metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass(),
false);
addAdapterMapMethod(ClassName.get("java.lang", "Object"), metadata.getTargetClassName(),
metadata.mapperClass(), true);
}
private void loadMapperConfig(MapperConfig mapperConfig) {
@ -411,7 +394,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
}
private void refreshProperties(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
final BuildCollator buildCollator = new BuildCollator(processingEnv, Constants.MAPPER_CONFIG_FILE_NAME);
final BuildCollator buildCollator = new BuildCollator(processingEnv, ContextConstants.MetaInf.mapperConfig);
// load previous mapper config
final List<TypeElement> typeElements = buildCollator.getRecords();
@ -423,12 +406,10 @@ public class AutoMapperProcessor extends AbstractProcessor {
// annotation --> MapperConfig
final TypeElement mapperConfigAnnotation =
processingEnv.getElementUtils().getTypeElement(MAPPER_CONFIG_ANNOTATION);
processingEnv.getElementUtils().getTypeElement(ContextConstants.Annotations.mapperConfig);
if (mapperConfigAnnotation != null) {
final Optional<? extends Element> mapperConfigOptional =
roundEnv.getElementsAnnotatedWith(mapperConfigAnnotation)
.stream()
.findFirst();
roundEnv.getElementsAnnotatedWith(mapperConfigAnnotation).stream().findFirst();
if (mapperConfigOptional.isPresent()) {
loadMapperConfig(mapperConfigOptional.get().getAnnotation(MapperConfig.class));
// record
@ -473,13 +454,10 @@ public class AutoMapperProcessor extends AbstractProcessor {
return;
}
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
new BuildCollator(processingEnv, Constants.AUTO_MAPPER_FILE_NAME));
new BuildCollator(processingEnv, ContextConstants.MetaInf.autoMapper));
final List<AutoMapperMetadata> autoMapperMetadataList = elements
.stream()
.map(this::buildAutoMapperMetadata)
.filter(Objects::nonNull)
.collect(Collectors.toList());
final List<AutoMapperMetadata> autoMapperMetadataList =
elements.stream().map(this::buildAutoMapperMetadata).filter(Objects::nonNull).collect(Collectors.toList());
mapperList.addAll(autoMapperMetadataList);
}
@ -489,8 +467,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
return;
}
final List<AutoMapperMetadata> autoMapperMetadata = getElementAndMergeHistory(roundEnv, annotation,
new BuildCollator(processingEnv, Constants.AUTO_MAPPERS_FILE_NAME))
.stream()
new BuildCollator(processingEnv, ContextConstants.MetaInf.autoMappers)).stream()
.map(this::buildAutoMapperMetadataByAutoMappers)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
@ -526,25 +503,46 @@ public class AutoMapperProcessor extends AbstractProcessor {
addAdapterMethod(metadata);
});
adapterMapperGenerator.write(processingEnv, methodMap.values(), AutoMapperProperties.getAdapterClassName());
adapterMapperGenerator.write(processingEnv,
methodMap.values(),
AutoMapperProperties.getAdapterClassName(),
false);
mapperConfigGenerator.write(processingEnv, AutoMapperProperties.getConfigClassName(),
AutoMapperProperties.getAdapterClassName(), customMapperList);
mapperConfigGenerator.write(processingEnv,
AutoMapperProperties.getConfigClassName(),
AutoMapperProperties.getAdapterClassName(),
customMapperList);
boolean needCycleAvoiding = methodMap.values().stream().anyMatch(
AbstractAdapterMethodMetadata::needCycleAvoiding);
if (needCycleAvoiding) {
adapterMapperGenerator.write(processingEnv,
methodMap.values(),
AutoMapperProperties.getCycleAvoidingAdapterClassName(),
true);
mapperConfigGenerator.write(processingEnv,
AutoMapperProperties.getCycleAvoidingConfigClassName(),
AutoMapperProperties.getCycleAvoidingAdapterClassName(),
customMapperList);
}
}
private AutoMapperMetadata reverseMapper(AutoMapperMetadata autoMapperMetadata) {
AutoMapperMetadata reverseMapperMetadata = initAutoMapperMetadata(
autoMapperMetadata.getTargetClassName(), autoMapperMetadata.getSourceClassName());
AutoMapperMetadata reverseMapperMetadata =
initAutoMapperMetadata(autoMapperMetadata.getTargetClassName(),
autoMapperMetadata.getSourceClassName(),
autoMapperMetadata.isCycles());
reverseMapperMetadata.setConvertGenerate(autoMapperMetadata.isReverseConvertGenerate());
reverseMapperMetadata.setUsesClassNameList(autoMapperMetadata.getUsesClassNameList());
reverseMapperMetadata.setImportsClassNameList(autoMapperMetadata.getImportsClassNameList());
reverseMapperMetadata.setMapstructConfigClass(
ClassName.get(AutoMapperProperties.getConfigPackage(), AutoMapperProperties.getConfigClassName()));
reverseMapperMetadata.setCycles(autoMapperMetadata.isCycles());
if (reverseMapperMetadata.isCycles()) {
reverseMapperMetadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseCycleAvoidingMapper"));
reverseMapperMetadata.setSuperClass(ClassName.get(ContextConstants.BaseCycleAvoidingMapper.packageName,
ContextConstants.BaseCycleAvoidingMapper.className));
} else {
reverseMapperMetadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseMapper"));
reverseMapperMetadata.setSuperClass(
ClassName.get(ContextConstants.BaseMapper.packageName, ContextConstants.BaseMapper.className));
}
if (CollectionUtil.isNotEmpty(autoMapperMetadata.getFieldReverseMappingList())) {
reverseMapperMetadata.setFieldMappingList(autoMapperMetadata.getFieldReverseMappingList());
@ -580,9 +578,9 @@ public class AutoMapperProcessor extends AbstractProcessor {
if (metadata == null) {
return;
}
AdapterMethodMetadata adapterMethodMetadata = AdapterMethodMetadata.newInstance(
metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass(),
metadata.isCycles());
AdapterMethodMetadata adapterMethodMetadata =
AdapterMethodMetadata.newInstance(metadata.getSourceClassName(), metadata.getTargetClassName(),
metadata.mapperClass(), metadata.isCycles());
methodMap.putIfAbsent(adapterMethodMetadata.getMethodName(), adapterMethodMetadata);
}
@ -592,14 +590,21 @@ public class AutoMapperProcessor extends AbstractProcessor {
mapMethodMap.putIfAbsent(adapterMapMethodMetadata.getMethodName(), adapterMapMethodMetadata);
}
private AutoMapperMetadata initAutoMapperMetadata(ClassName source, ClassName target) {
private AutoMapperMetadata initAutoMapperMetadata(ClassName source, ClassName target, boolean cycles) {
AutoMapperMetadata metadata = new AutoMapperMetadata();
metadata.setSourceClassName(source);
metadata.setTargetClassName(target);
metadata.setSuperGenerics(new ClassName[] {source, target});
metadata.setMapstructConfigClass(
ClassName.get(AutoMapperProperties.getConfigPackage(), AutoMapperProperties.getConfigClassName()));
ClassName mapStructConfigClass;
if (cycles) {
mapStructConfigClass = ClassName.get(AutoMapperProperties.getConfigPackage(),
AutoMapperProperties.getCycleAvoidingConfigClassName());
} else {
mapStructConfigClass = ClassName.get(AutoMapperProperties.getConfigPackage(),
AutoMapperProperties.getConfigClassName());
}
metadata.setMapstructConfigClass(mapStructConfigClass);
return metadata;
}
@ -609,16 +614,13 @@ public class AutoMapperProcessor extends AbstractProcessor {
return null;
}
Set<String> targetClassNames = new HashSet<>();
return Arrays.stream(autoMappers.value())
.filter(autoMapper -> {
ClassName className = transToClassName(autoMapper::target);
if (className == null) {
return false;
}
return targetClassNames.add(className.reflectionName());
})
.map(autoMapper -> buildAutoMapperMetadata(autoMapper, ele))
.collect(Collectors.toList());
return Arrays.stream(autoMappers.value()).filter(autoMapper -> {
ClassName className = transToClassName(autoMapper::target);
if (className == null) {
return false;
}
return targetClassNames.add(className.reflectionName());
}).map(autoMapper -> buildAutoMapperMetadata(autoMapper, ele)).collect(Collectors.toList());
}
private AutoMapperMetadata buildAutoMapperMetadata(final Element ele) {
@ -670,7 +672,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
List<AutoMappingMetadata> reverseMappingMetadataList = buildFieldReverseMappingMetadata((TypeElement) ele);
reverseMappingMetadataList.removeIf(mappingMetadata -> !isTargetFieldMapping(target, mappingMetadata));
AutoMapperMetadata metadata = initAutoMapperMetadata(source, target);
AutoMapperMetadata metadata = initAutoMapperMetadata(source, target, autoMapper.cycles());
metadata.setUsesClassNameList(uses);
metadata.setImportsClassNameList(importsClassNameList);
@ -680,9 +682,11 @@ public class AutoMapperProcessor extends AbstractProcessor {
metadata.setReverseConvertGenerate(autoMapper.reverseConvertGenerate());
metadata.setCycles(autoMapper.cycles());
if (metadata.isCycles()) {
metadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseCycleAvoidingMapper"));
metadata.setSuperClass(ClassName.get(ContextConstants.BaseCycleAvoidingMapper.packageName,
ContextConstants.BaseCycleAvoidingMapper.className));
} else {
metadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseMapper"));
metadata.setSuperClass(
ClassName.get(ContextConstants.BaseMapper.packageName, ContextConstants.BaseMapper.className));
}
addMapper(metadata);
@ -701,27 +705,24 @@ public class AutoMapperProcessor extends AbstractProcessor {
}
ReverseAutoMapping reverseAutoMapping = field.getAnnotation(ReverseAutoMapping.class);
if (reverseAutoMapping != null) {
list.add(buildAutoMappingMetadata(reverseAutoMapping, field, ele));
list.add(buildAutoMappingMetadata(reverseAutoMapping, field));
}
ReverseAutoMappings reverseAutoMappings = field.getAnnotation(ReverseAutoMappings.class);
if (reverseAutoMappings != null) {
for (ReverseAutoMapping mapping : reverseAutoMappings.value()) {
list.add(buildAutoMappingMetadata(mapping, field, ele));
list.add(buildAutoMappingMetadata(mapping, field));
}
}
}
// super class
getSuperClass(ele)
.ifPresent(superClass -> list.addAll(buildFieldReverseMappingMetadata(superClass)));
getSuperClass(ele).ifPresent(superClass -> list.addAll(buildFieldReverseMappingMetadata(superClass)));
list.removeIf(Objects::isNull);
return list;
}
private AutoMappingMetadata buildAutoMappingMetadata(ReverseAutoMapping reverseAutoMapping,
Element ele,
TypeElement type) {
private AutoMappingMetadata buildAutoMappingMetadata(ReverseAutoMapping reverseAutoMapping, Element ele) {
ClassName targetClass = transToClassName(reverseAutoMapping::targetClass);
if (targetClass == null) {
return null;
@ -751,10 +752,10 @@ public class AutoMapperProcessor extends AbstractProcessor {
private void addMapper(AutoMapperMetadata metadata) {
if (!mapperSet.add(metadata.mapperName())) {
throw new DuplicateMapperException("An exception occurred to generate " + metadata.mapperName()
+ ", check the mapping configuration for "
+ metadata.getSourceClassName().reflectionName()
+ " or " + metadata.getTargetClassName().reflectionName());
throw new DuplicateMapperException("An exception occurred to generate " + metadata.mapperName() +
", check the mapping configuration for " +
metadata.getSourceClassName().reflectionName() + " or " +
metadata.getTargetClassName().reflectionName());
}
}
@ -771,25 +772,24 @@ public class AutoMapperProcessor extends AbstractProcessor {
}
AutoMapping autoMapping = ele.getAnnotation(AutoMapping.class);
if (autoMapping != null) {
list.add(buildAutoMappingMetadata(autoMapping, ele, autoMapperEle));
list.add(buildAutoMappingMetadata(autoMapping, ele));
}
final AutoMappings autoMappings = ele.getAnnotation(AutoMappings.class);
if (autoMappings != null) {
for (AutoMapping autoMappingEle : autoMappings.value()) {
list.add(buildAutoMappingMetadata(autoMappingEle, ele, autoMapperEle));
list.add(buildAutoMappingMetadata(autoMappingEle, ele));
}
}
}
// add super class AutoMappings
getSuperClass(autoMapperEle)
.ifPresent(superClass -> list.addAll(buildFieldMappingMetadata(superClass)));
getSuperClass(autoMapperEle).ifPresent(superClass -> list.addAll(buildFieldMappingMetadata(superClass)));
list.removeIf(Objects::isNull);
return list;
}
private AutoMappingMetadata buildAutoMappingMetadata(AutoMapping autoMapping, Element ele, TypeElement type) {
private AutoMappingMetadata buildAutoMappingMetadata(AutoMapping autoMapping, Element ele) {
ClassName targetClass = transToClassName(autoMapping::targetClass);
if (targetClass == null) {
return null;

View File

@ -1,17 +1,14 @@
package io.github.linpeilie.processor;
import org.mapstruct.Builder;
import org.mapstruct.NullValueMappingStrategy;
import org.mapstruct.NullValuePropertyMappingStrategy;
import org.mapstruct.ReportingPolicy;
import static io.github.linpeilie.processor.Constants.*;
public class AutoMapperProperties {
private static String mapperPackage;
private static String componentModel = DEFAULT_COMPONENT_MODEL;
private static String componentModel = ContextConstants.ComponentModelConfig.defaultComponentModel;
private static ReportingPolicy unmappedSourcePolicy = ReportingPolicy.IGNORE;
@ -25,17 +22,19 @@ public class AutoMapperProperties {
private static boolean disableBuilder = true;
private static String adapterPackage = DEFAULT_BASE_PACKAGE;
private static String adapterPackage = ContextConstants.ConvertAdapter.packageName;
private static String adapterClassName = DEFAULT_ADAPTER_CLASS_NAME;
private static String adapterClassName = ContextConstants.ConvertAdapter.convertMapperAdapterClassName;
private static String mapAdapterClassName = DEFAULT_MAP_ADAPTER_CLASS_NAME;
private static String mapAdapterClassName = ContextConstants.ConvertAdapter.mapConvertMapperAdapterClassName;
private static String autoConfigPackage = DEFAULT_BASE_PACKAGE;
private static String autoConfigPackage = ContextConstants.AutoConfig.packageName;
private static String autoMapperConfigClassName = AUTO_MAPPER_CONFIG_CLASS_NAME;
private static String autoMapperConfigClassName = ContextConstants.AutoConfig.autoMapperConfigClassName;
private static String autoMapMapperConfigClassName = AUTO_MAP_MAPPER_CONFIG_CLASS_NAME;
private static String autoMapMapperConfigClassName = ContextConstants.AutoConfig.autoMapMapperConfigClassName;
/* ******************** getter/setter ******************** */
public static String getMapperPackage() {
return mapperPackage;
@ -53,6 +52,10 @@ public class AutoMapperProperties {
return adapterClassName;
}
public static String getCycleAvoidingAdapterClassName() {
return getAdapterClassName() + "ForCycleAvoiding";
}
public static void setAdapterClassName(final String adapterClassName) {
AutoMapperProperties.adapterClassName = adapterClassName;
}
@ -77,6 +80,10 @@ public class AutoMapperProperties {
return autoMapperConfigClassName;
}
public static String getCycleAvoidingConfigClassName() {
return getConfigClassName() + "ForCycleAvoiding";
}
public static void setAutoMapperConfigClassName(String autoMapperConfigClassName) {
AutoMapperProperties.autoMapperConfigClassName = autoMapperConfigClassName;
}

View File

@ -38,7 +38,7 @@ public class BuildCollator {
Filer filer = processingEnv.getFiler();
try {
FileObject fileObject = filer.getResource(StandardLocation.CLASS_OUTPUT, "",
Constants.MAPSTRUCT_PLUS_META_INF + fileName);
ContextConstants.MetaInf.folder + fileName);
this.collatorFile = new File(fileObject.getName());
if (collatorFile.exists()) {
records = FileUtil.readUtf8Lines(collatorFile)

View File

@ -1,52 +0,0 @@
package io.github.linpeilie.processor;
import java.io.File;
import org.mapstruct.MappingConstants;
public class Constants {
public static final String DEFAULT_BASE_PACKAGE = "io.github.linpeilie";
public static final String DEFAULT_COMPONENT_MODEL = MappingConstants.ComponentModel.SPRING;
public static final String DEFAULT_ADAPTER_CLASS_NAME = "ConvertMapperAdapter";
public static final String DEFAULT_MAP_ADAPTER_CLASS_NAME = "MapConvertMapperAdapter";
public static final String AUTO_MAPPER_CONFIG_CLASS_NAME = "AutoMapperConfig";
public static final String AUTO_MAP_MAPPER_CONFIG_CLASS_NAME = "AutoMapMapperConfig";
public static final String AUTO_MAPPER_ANNOTATION = "io.github.linpeilie.annotations.AutoMapper";
public static final String AUTO_MAPPERS_ANNOTATION = "io.github.linpeilie.annotations.AutoMappers";
public static final String AUTO_MAP_MAPPER_ANNOTATION = "io.github.linpeilie.annotations.AutoMapMapper";
public static final String AUTO_ENUM_MAPPER_ANNOTATION = "io.github.linpeilie.annotations.AutoEnumMapper";
public static final String MAPPER_CONFIG_ANNOTATION = "io.github.linpeilie.annotations.MapperConfig";
public static final String MAPPER_ANNOTATION = "org.mapstruct.Mapper";
public static final String COMPONENT_MODEL_CONFIG_ANNOTATION = "io.github.linpeilie.annotations.ComponentModelConfig";
public static final String MAPSTRUCT_MAPPER_PACKAGE = "org.mapstruct";
public static final String MAPSTRUCT_MAPPER_CLASS_NAME = "Mapper";
public static final String MAPSTRUCT_PLUS_META_INF = "META-INF/mps/";
public static final String MAPPER_CONFIG_FILE_NAME = "config";
public static final String MAPPERS_FILE_NAME = "mappers";
public static final String AUTO_MAPPER_FILE_NAME = "autoMapper";
public static final String AUTO_MAPPERS_FILE_NAME = "autoMappers";
public static final String AUTO_MAP_MAPPERS_FILE_NAME = "autoMapMappers";
public static final String ENUM_MAPPERS_FILE_NAME = "enumMappers";
}

View File

@ -0,0 +1,107 @@
package io.github.linpeilie.processor;
import org.mapstruct.MappingConstants;
/**
* 上下文常量
*/
public interface ContextConstants {
/**
* BaseMapper 接口
*/
interface BaseMapper {
String packageName = "io.github.linpeilie";
String className = "BaseMapper";
}
/**
* BaseMapMapper 接口
*/
interface BaseMapMapper {
String packageName = "io.github.linpeilie";
String className = "BaseMapMapper";
}
/**
* BaseCycleAvoidingMapper 接口
*/
interface BaseCycleAvoidingMapper {
String packageName = "io.github.linpeilie";
String className = "BaseCycleAvoidingMapper";
}
interface Mapper {
String qualifiedClassName = "org.mapstruct.Mapper";
String packageName = "org.mapstruct";
String className = "Mapper";
}
interface AutoMapper {
String qualifiedClassName = "io.github.linpeilie.annotations.AutoMapper";
}
interface AutoMappers {
String qualifiedClassName = "io.github.linpeilie.annotations.AutoMappers";
}
interface AutoMapMapper {
String qualifiedClassName = "io.github.linpeilie.annotations.AutoMapMapper";
}
interface AutoEnumMapper {
String qualifiedClassName = "io.github.linpeilie.annotations.AutoEnumMapper";
}
interface MapperConfig {
String qualifiedClassName = "io.github.linpeilie.annotations.MapperConfig";
}
interface ComponentModelConfig {
String qualifiedClassName = "io.github.linpeilie.annotations.ComponentModelConfig";
String defaultComponentModel = MappingConstants.ComponentModel.SPRING;
}
interface ConvertAdapter {
String packageName = "io.github.linpeilie";
String convertMapperAdapterClassName = "ConverterMapperAdapter";
String mapConvertMapperAdapterClassName = "MapConvertMapperAdapter";
}
interface AutoConfig {
String packageName = "io.github.linpeilie";
String autoMapperConfigClassName = "AutoMapperConfig";
String autoMapMapperConfigClassName = "AutoMapMapperConfig";
}
interface Annotations {
String mapper = Mapper.qualifiedClassName;
String autoMapper = AutoMapper.qualifiedClassName;
String autoMappers = AutoMappers.qualifiedClassName;
String autoMapMapper = AutoMapMapper.qualifiedClassName;
String autoEnumMapper = AutoEnumMapper.qualifiedClassName;
String mapperConfig = MapperConfig.qualifiedClassName;
String componentModel = ComponentModelConfig.qualifiedClassName;
}
interface MetaInf {
String folder = "META-INF/mps/";
String mapperConfig = "config";
String mappers = "mappers";
String autoMapper = "autoMapper";
String autoMappers = "autoMappers";
String autoMapMappers = "autoMapMappers";
String enumMappers = "enumMappers";
}
interface Map {
String packageName = "java.util";
String className = "Map";
}
interface MapObjectConvert {
String packageName = "io.github.linpeilie.map";
String className = "MapObjectConvert";
}
}

View File

@ -10,13 +10,13 @@ import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import io.github.linpeilie.processor.ContextConstants;
import io.github.linpeilie.processor.metadata.AutoMapperMetadata;
import io.github.linpeilie.processor.metadata.AutoMappingMetadata;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -27,21 +27,15 @@ import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import org.apache.commons.lang3.StringUtils;
import static io.github.linpeilie.processor.Constants.*;
public class AutoMapperGenerator {
public static final String CONVERT_METHOD_NAME = "convert";
public static final String CONVERT_WITH_CYCLE_METHOD_NAME = "convertWithCycle";
public void write(AutoMapperMetadata metadata, final ProcessingEnvironment processingEnv, Writer writer) {
try {
JavaFile.builder(metadata.mapperPackage(), createTypeSpec(processingEnv, metadata)).build().writeTo(writer);
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (Exception e) {
throw e;
}
}
@ -51,7 +45,6 @@ public class AutoMapperGenerator {
final ClassName targetClassName = metadata.getTargetClassName();
TypeSpec.Builder builder = TypeSpec.interfaceBuilder(metadata.mapperName())
.addSuperinterface(converterName)
.addModifiers(Modifier.PUBLIC)
@ -61,33 +54,48 @@ public class AutoMapperGenerator {
final ParameterSpec target = ParameterSpec.builder(targetClassName, "target")
.addAnnotation(AnnotationSpec.builder(ClassName.get("org.mapstruct", "MappingTarget")).build())
.build();
final ParameterSpec context =
ParameterSpec.builder(ClassName.get("io.github.linpeilie", "CycleAvoidingMappingContext"), "context")
.addAnnotation(ClassName.get("org.mapstruct", "Context"))
.build();
if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) {
builder.addMethod(addConvertMethodSpec(Collections.singletonList(source), metadata.getFieldMappingList(),
targetClassName, metadata.isCycles()));
builder.addMethod(addConvertMethodSpec(
metadata.isCycles() ? CollectionUtil.newArrayList(source, context) : Collections.singletonList(source),
metadata.getFieldMappingList(),
targetClassName,
CONVERT_METHOD_NAME));
}
boolean targetIsImmutable = classIsImmutable(processingEnv, targetClassName);
if (targetIsImmutable) {
builder.addMethod(addEmptyConvertMethodForImmutableEntity(source, target, targetClassName, metadata.isCycles()));
} else {
builder.addMethod(addConvertMethodSpec(Arrays.asList(source, target), metadata.getFieldMappingList(),
targetClassName, metadata.isCycles()));
builder.addMethod(
addEmptyConvertMethodForImmutableEntity(
metadata.isCycles() ? CollectionUtil.newArrayList(source, target,
context) : CollectionUtil.newArrayList(source, target),
targetClassName,
CONVERT_METHOD_NAME));
} else if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) {
builder.addMethod(addConvertMethodSpec(
metadata.isCycles() ? CollectionUtil.newArrayList(source, target,
context) : CollectionUtil.newArrayList(source, target),
metadata.getFieldMappingList(),
targetClassName,
CONVERT_METHOD_NAME));
}
return builder.build();
}
private MethodSpec addEmptyConvertMethodForImmutableEntity(ParameterSpec source,
ParameterSpec target,
ClassName targetClassName, boolean cycles) {
String methodName = cycles ? CONVERT_WITH_CYCLE_METHOD_NAME : CONVERT_METHOD_NAME;
return MethodSpec.methodBuilder(methodName)
private MethodSpec addEmptyConvertMethodForImmutableEntity(List<ParameterSpec> parameterSpecs,
ClassName targetClassName,
String methodName) {
MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
.addParameter(source)
.addParameter(target)
.addParameters(parameterSpecs)
.returns(targetClassName)
.addCode("return target;")
.build();
.addCode("return target;");
return builder.build();
}
private boolean classIsImmutable(ProcessingEnvironment processingEnv, ClassName className) {
@ -103,9 +111,8 @@ public class AutoMapperGenerator {
}
private MethodSpec addConvertMethodSpec(List<ParameterSpec> parameterSpecs,
List<AutoMappingMetadata> autoMappingMetadataList,
ClassName target, boolean cycles) {
String methodName = cycles ? CONVERT_WITH_CYCLE_METHOD_NAME : CONVERT_METHOD_NAME;
List<AutoMappingMetadata> autoMappingMetadataList,
ClassName target, String methodName) {
final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodName)
.addParameters(parameterSpecs)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
@ -140,10 +147,12 @@ public class AutoMapperGenerator {
builder.addMember("source", CodeBlock.builder().add("$S", autoMappingMetadata.getSource()).build());
}
if (StringUtils.isNotEmpty(autoMappingMetadata.getDefaultExpression())) {
builder.addMember("defaultExpression",CodeBlock.builder().add("$S", autoMappingMetadata.getDefaultExpression()).build());
builder.addMember("defaultExpression",
CodeBlock.builder().add("$S", autoMappingMetadata.getDefaultExpression()).build());
}
if (StringUtils.isNotEmpty(autoMappingMetadata.getConditionExpression())) {
builder.addMember("conditionExpression", CodeBlock.builder().add("$S", autoMappingMetadata.getConditionExpression()).build());
builder.addMember("conditionExpression",
CodeBlock.builder().add("$S", autoMappingMetadata.getConditionExpression()).build());
}
return builder.build();
}).collect(Collectors.toList());
@ -182,7 +191,8 @@ public class AutoMapperGenerator {
final CodeBlock importsCodeBlock = importsCodeBuilder.add("}").build();
AnnotationSpec.Builder builder =
AnnotationSpec.builder(ClassName.get(MAPSTRUCT_MAPPER_PACKAGE, MAPSTRUCT_MAPPER_CLASS_NAME))
AnnotationSpec.builder(
ClassName.get(ContextConstants.Mapper.packageName, ContextConstants.Mapper.className))
.addMember("config", configCodeBlock)
.addMember("uses", usesCodeBlock)
.addMember("imports", importsCodeBlock);

View File

@ -12,31 +12,17 @@ import javax.lang.model.element.Modifier;
public class DefaultAdapterMapperGenerator extends AbstractAdapterMapperGenerator {
public TypeSpec createTypeSpec(Collection<AbstractAdapterMethodMetadata> adapterMethods, String adapterClassName) {
TypeSpec.Builder adapterBuilder = TypeSpec.classBuilder(
ClassName.get(adapterPackage(), adapterClassName))
.addModifiers(Modifier.PUBLIC);
adapterMethods.forEach(adapterMethod -> adapterBuilder.addMethods(buildProxyMethod(adapterMethod)));
return adapterBuilder.build();
}
private String firstWordToLower(String str) {
return str.substring(0, 1).toLowerCase() + str.substring(1);
}
@Override
protected CodeBlock proxyMethodTarget(final AbstractAdapterMethodMetadata adapterMethodMetadata) {
return CodeBlock.of("return ($T.getMapper($T.class)).$N($N);",
ClassName.get("org.mapstruct.factory", "Mappers"), adapterMethodMetadata.getMapper(),
adapterMethodMetadata.getMapperMethodName(), "param");
adapterMethodMetadata.getMapperMethodName(), PARAM__PARAMETER_NAME);
}
@Override
protected CodeBlock cycleAvoidingMethodTarget(AbstractAdapterMethodMetadata adapterMethodMetadata) {
return CodeBlock.of("return ($T.getMapper($T.class)).$N($N, $N);",
ClassName.get("org.mapstruct.factory", "Mappers"), adapterMethodMetadata.getMapper(),
adapterMethodMetadata.cycleAvoidingMethodName(), "param", "context");
adapterMethodMetadata.cycleAvoidingMethodName(), PARAM__PARAMETER_NAME, CONTEXT__PARAMETER_NAME);
}
}

View File

@ -4,40 +4,44 @@ import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import io.github.linpeilie.processor.AbstractAdapterMapperGenerator;
import io.github.linpeilie.processor.metadata.AbstractAdapterMethodMetadata;
import java.util.Collection;
import java.util.List;
import javax.lang.model.element.Modifier;
public abstract class IocAdapterMapperGenerator extends AbstractAdapterMapperGenerator {
protected static final String CONVERTER_FIELD_NAME = "converter";
protected abstract AnnotationSpec componentAnnotation();
protected abstract List<AnnotationSpec> injectAnnotations();
@Override
protected TypeSpec createTypeSpec(final Collection<AbstractAdapterMethodMetadata> adapterMethods,
final String adapterClassName) {
protected TypeSpec createTypeSpec(List<MethodSpec> methods, String adapterClassName, ClassName superClass) {
TypeSpec.Builder adapterBuilder = TypeSpec.classBuilder(ClassName.get(adapterPackage(), adapterClassName))
.addModifiers(Modifier.PUBLIC)
.addAnnotation(componentAnnotation());
adapterMethods.stream()
.filter(adapterMethodMetadata -> !adapterMethodMetadata.isStatic())
.map(AbstractAdapterMethodMetadata::getMapper)
.distinct()
.forEach(mapper -> adapterBuilder.addField(buildMapperField(mapper)));
adapterBuilder.addField(buildConverterField());
adapterMethods.forEach(adapterMethod -> adapterBuilder
.addMethods(buildProxyMethod(adapterMethod)));
adapterBuilder.addMethods(methods);
if (superClass != null) {
adapterBuilder.superclass(superClass);
}
return adapterBuilder.build();
}
private FieldSpec buildMapperField(ClassName mapper) {
return FieldSpec.builder(mapper, firstWordToLower(mapper.simpleName()), Modifier.PRIVATE)
private FieldSpec buildConverterField() {
return FieldSpec.builder(
ClassName.get("io.github.linpeilie", "Converter"),
CONVERTER_FIELD_NAME,
Modifier.PRIVATE
)
.addAnnotations(injectAnnotations())
.build();
}
@ -46,21 +50,33 @@ public abstract class IocAdapterMapperGenerator extends AbstractAdapterMapperGen
return str.substring(0, 1).toLowerCase() + str.substring(1);
}
/**
* <code>
* return converter.convert(param, Target.class);
* </code>
*/
@Override
protected CodeBlock proxyMethodTarget(AbstractAdapterMethodMetadata adapterMethodMetadata) {
return CodeBlock.builder()
.add("return $N.$N($N);", firstWordToLower(adapterMethodMetadata.getMapper().simpleName()),
adapterMethodMetadata.getMapperMethodName(),
"param")
.add("return $N.convert($N, $T.class);\n", CONVERTER_FIELD_NAME,
PARAM__PARAMETER_NAME,
adapterMethodMetadata.getReturn())
.build();
}
/**
* <code>
* return converter.convert(param, Target.class, context);
* </code>
*/
@Override
protected CodeBlock cycleAvoidingMethodTarget(AbstractAdapterMethodMetadata adapterMethodMetadata) {
return CodeBlock.builder()
.add("return $N.$N($N, $N);", firstWordToLower(adapterMethodMetadata.getMapper().simpleName()),
adapterMethodMetadata.cycleAvoidingMethodName(),
"param", "context")
.add("return $N.convert($N, $T.class, $N);\n",
CONVERTER_FIELD_NAME,
PARAM__PARAMETER_NAME,
adapterMethodMetadata.getReturn(),
CONTEXT__PARAMETER_NAME)
.build();
}
}

View File

@ -28,12 +28,6 @@ public class SpringAdapterMapperGenerator extends IocAdapterMapperGenerator {
.build();
}
private AnnotationSpec lazy() {
return AnnotationSpec
.builder(ClassName.get("org.springframework.context.annotation", "Lazy"))
.build();
}
@Override
protected AnnotationSpec componentAnnotation() {
return component();
@ -41,7 +35,7 @@ public class SpringAdapterMapperGenerator extends IocAdapterMapperGenerator {
@Override
protected List<AnnotationSpec> injectAnnotations() {
return CollectionUtil.newArrayList(autowired(), lazy());
return CollectionUtil.newArrayList(autowired());
}
}

View File

@ -1,6 +1,7 @@
package io.github.linpeilie.processor.metadata;
import com.squareup.javapoet.ClassName;
import io.github.linpeilie.processor.utils.ClassUtil;
public class AdapterMethodMetadata extends AbstractAdapterMethodMetadata {
@ -19,9 +20,9 @@ public class AdapterMethodMetadata extends AbstractAdapterMethodMetadata {
@Override
public String getMethodName() {
final String sourceName = source.toString().replace(".", "_");
return sourceName.substring(0, 1).toLowerCase() + sourceName.substring(1) + "To" +
target.simpleName();
String source = ClassUtil.simplifyQualifiedName(this.source.toString());
source = source.substring(0, 1).toLowerCase() + source.substring(1);
return source + "To" + target.simpleName();
}
public ClassName getTarget() {

View File

@ -0,0 +1,24 @@
package io.github.linpeilie.processor.utils;
public class ClassUtil {
/**
* 简化类全限定名
* @param qualifiedName
* @return
*/
public static String simplifyQualifiedName(String qualifiedName) {
String[] arr = qualifiedName.split("\\.");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if (i == arr.length - 1) {
sb.append("_");
sb.append(arr[i]);
} else {
sb.append(arr[i].charAt(0));
}
}
return sb.toString();
}
}

View File

@ -1,6 +1,7 @@
package io.github.linpeilie.mapstruct;
import io.github.linpeilie.AbstractCachedConverterFactory;
import io.github.linpeilie.BaseCycleAvoidingMapper;
import io.github.linpeilie.BaseMapMapper;
import io.github.linpeilie.BaseMapper;
import org.springframework.context.ApplicationContext;

View File

@ -6,18 +6,20 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
private final ConcurrentHashMap<String, BaseMapper> mapperMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, BaseCycleAvoidingMapper> cycleAvoidingMapper = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, BaseMapMapper> mapMapperMap = new ConcurrentHashMap<>();
@Override
@SuppressWarnings("unchecked")
public <S, T> BaseMapper<S, T> getMapper(final Class<S> sourceType, final Class<T> targetType) {
final Class<?> source = wrapperClass(sourceType);
final Class<?> target = wrapperClass(targetType);
final String key = key(source, target);
Class<?> source = wrapperClass(sourceType);
Class<?> target = wrapperClass(targetType);
String key = key(source, target);
if (mapperMap.containsKey(key)) {
return mapperMap.get(key);
}
final BaseMapper mapper = findMapper(source, target);
BaseMapper mapper = findMapper(source, target);
if (mapper != null) {
mapperMap.put(key, mapper);
return mapper;
@ -25,6 +27,18 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
return null;
}
@Override
public <S, T> BaseCycleAvoidingMapper<S, T> getCycleAvoidingMapper(Class<S> sourceType, Class<T> targetType) {
BaseMapper<S, T> mapper = getMapper(sourceType, targetType);
if (mapper == null) {
return null;
}
if (mapper instanceof BaseCycleAvoidingMapper) {
return (BaseCycleAvoidingMapper<S, T>) mapper;
}
return null;
}
@Override
public <S> BaseMapMapper<S> getMapMapper(final Class<S> sourceType) {
final Class<?> source = wrapperClass(sourceType);
@ -44,7 +58,7 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
return clazz;
}
protected abstract <S, T> BaseMapper findMapper(final Class<S> source, final Class<T> target);
protected abstract <S, T> BaseMapper findMapper(Class<S> source, Class<T> target);
protected abstract <S> BaseMapMapper findMapMapper(final Class<?> source);

View File

@ -3,34 +3,42 @@ package io.github.linpeilie;
import cn.hutool.core.collection.CollectionUtil;
import io.github.linpeilie.annotations.DoIgnore;
import java.util.List;
import java.util.stream.Collectors;
import org.mapstruct.Context;
import org.mapstruct.MappingTarget;
public interface BaseCycleAvoidingMapper<S, T> extends BaseMapper<S, T> {
@DoIgnore
T convertWithCycle(S source, @Context CycleAvoidingMappingContext context);
T convert(S source, @Context CycleAvoidingMappingContext context);
@DoIgnore
T convertWithCycle(S source, @MappingTarget T target, @Context CycleAvoidingMappingContext context);
T convert(S source, @MappingTarget T target, @Context CycleAvoidingMappingContext context);
List<T> convertWithCycle(List<S> sourceList, @Context CycleAvoidingMappingContext context);
@Override
@DoIgnore
default List<T> convert(List<S> sourceList) {
return convertWithCycle(sourceList, new CycleAvoidingMappingContext());
default List<T> convert(List<S> sourceList, @Context CycleAvoidingMappingContext context) {
return sourceList.stream()
.map(item -> convert(item, context))
.collect(Collectors.toList());
}
@Override
@DoIgnore
default T convert(S source) {
return convertWithCycle(source, new CycleAvoidingMappingContext());
return convert(source, new CycleAvoidingMappingContext());
}
@Override
@DoIgnore
default T convert(S source, T target) {
return convertWithCycle(source, new CycleAvoidingMappingContext());
default T convert(S source, @MappingTarget T target) {
return convert(source, new CycleAvoidingMappingContext());
}
@Override
@DoIgnore
default List<T> convert(List<S> sourceList) {
return convert(sourceList, new CycleAvoidingMappingContext());
}
}

View File

@ -6,14 +6,4 @@ public interface BaseMapMapper<T> {
T convert(Map<String, Object> map);
default T convertByObj(Object obj) {
if (obj == null) {
return null;
}
if (obj instanceof Map) {
return convert((Map<String, Object>) obj);
}
return null;
}
}

View File

@ -16,6 +16,7 @@ public interface BaseMapper<S, T> {
@DoIgnore
T convert(S source, @MappingTarget T target);
@DoIgnore
default List<T> convert(List<S> sourceList) {
if (CollectionUtil.isEmpty(sourceList)) {
return new ArrayList<>();

View File

@ -48,12 +48,36 @@ public class Converter {
}
public <S, T> List<T> convert(List<S> source, Class<T> targetType) {
if (source == null || source.size() == 0) {
if (source == null || source.isEmpty()) {
return new ArrayList<>();
}
return source.stream().map(item -> convert(item, targetType)).collect(Collectors.toList());
}
@SuppressWarnings("unchecked")
public <S, T> T convert(S source, Class<T> target, CycleAvoidingMappingContext context) {
if (source == null) {
return null;
}
BaseCycleAvoidingMapper<S, T> mapper =
(BaseCycleAvoidingMapper<S, T>) converterFactory.getCycleAvoidingMapper(source.getClass(), target);
if (mapper != null) {
return mapper.convert(source, context);
}
throw new ConvertException("cannot find converter from " + source.getClass().getSimpleName() + " to " +
target.getSimpleName());
}
public <S, T> List<T> convert(List<S> source, Class<T> targetType, CycleAvoidingMappingContext context) {
if (source == null || source.isEmpty()) {
return new ArrayList<>();
}
return source.stream().map(item -> convert(item, targetType, context)).collect(Collectors.toList());
}
public <T> T convert(Map<String, Object> map, Class<T> target) {
if (map == null || map.isEmpty()) {
return null;

View File

@ -4,6 +4,8 @@ public interface ConverterFactory {
<S, T> BaseMapper<S, T> getMapper(Class<S> sourceType, Class<T> targetType);
<S, T> BaseCycleAvoidingMapper<S, T> getCycleAvoidingMapper(Class<S> sourceType, Class<T> targetType);
<S> BaseMapMapper<S> getMapMapper(Class<S> sourceType);
}