diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Dept.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Dept.java new file mode 100644 index 0000000..2ee6bd1 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Dept.java @@ -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 children; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/DeptDTO.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/DeptDTO.java new file mode 100644 index 0000000..9d59db5 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/DeptDTO.java @@ -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 children; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee1.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee1.java new file mode 100644 index 0000000..a19a772 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee1.java @@ -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; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee1Dto.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee1Dto.java new file mode 100644 index 0000000..98db85a --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee1Dto.java @@ -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; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductProperty.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductProperty.java index a297e64..d254959 100644 --- a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductProperty.java +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductProperty.java @@ -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; diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductPropertyDto.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductPropertyDto.java index f6a90ca..79a0ed7 100644 --- a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductPropertyDto.java +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductPropertyDto.java @@ -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; diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AbstractAdapterMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AbstractAdapterMapperGenerator.java index 10b4c32..9ca5cc4 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AbstractAdapterMapperGenerator.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AbstractAdapterMapperGenerator.java @@ -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 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 adapterMethods, - String adapterClassName); + protected TypeSpec createAdapterTypeSpec(String adapterClassName, + Collection adapterMethods, + boolean cycle) { + List 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 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 buildProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) { + private List buildProxyMethod(AbstractAdapterMethodMetadata adapterMethod, boolean cycle) { + List methodSpecs = new ArrayList<>(); + if (cycle) { + methodSpecs.addAll(buildCycleAvoidingProxyMethod(adapterMethod)); + } else { + methodSpecs.addAll(buildDefaultProxyMethod(adapterMethod, null)); + } + return methodSpecs; + } + + protected List buildDefaultProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata, + ClassName annotation) { List 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 buildCycleAvoidingProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) { + + // default impl + List defaultMethods = buildDefaultProxyMethod(adapterMethodMetadata, + ClassName.get("io.github.linpeilie.annotations", "DoIgnore")); + List 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(); } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProcessor.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProcessor.java index 3c8d581..dd89a7a 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProcessor.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProcessor.java @@ -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 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 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 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 uses = Arrays.asList(ClassName.get("io.github.linpeilie.map", "MapObjectConvert")); + List 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 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 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 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 elements = getElementAndMergeHistory(roundEnv, annotation, - new BuildCollator(processingEnv, Constants.AUTO_MAPPER_FILE_NAME)); + new BuildCollator(processingEnv, ContextConstants.MetaInf.autoMapper)); - final List autoMapperMetadataList = elements - .stream() - .map(this::buildAutoMapperMetadata) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + final List 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 = 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 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 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; diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProperties.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProperties.java index 1a72ec8..aa98fd1 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProperties.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/AutoMapperProperties.java @@ -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; } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/BuildCollator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/BuildCollator.java index 39f4a16..a47aaa1 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/BuildCollator.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/BuildCollator.java @@ -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) diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/Constants.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/Constants.java deleted file mode 100644 index 58ee910..0000000 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/Constants.java +++ /dev/null @@ -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"; - -} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/ContextConstants.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/ContextConstants.java new file mode 100644 index 0000000..7d9eb4f --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/ContextConstants.java @@ -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"; + } + +} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/AutoMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/AutoMapperGenerator.java index 17b3c12..5d5378c 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/AutoMapperGenerator.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/AutoMapperGenerator.java @@ -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 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 parameterSpecs, - List autoMappingMetadataList, - ClassName target, boolean cycles) { - String methodName = cycles ? CONVERT_WITH_CYCLE_METHOD_NAME : CONVERT_METHOD_NAME; + List 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); diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/DefaultAdapterMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/DefaultAdapterMapperGenerator.java index 3ef885c..67c604c 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/DefaultAdapterMapperGenerator.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/DefaultAdapterMapperGenerator.java @@ -12,31 +12,17 @@ import javax.lang.model.element.Modifier; public class DefaultAdapterMapperGenerator extends AbstractAdapterMapperGenerator { - public TypeSpec createTypeSpec(Collection 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); } } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/IocAdapterMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/IocAdapterMapperGenerator.java index d880a3d..e6d4930 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/IocAdapterMapperGenerator.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/IocAdapterMapperGenerator.java @@ -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 injectAnnotations(); @Override - protected TypeSpec createTypeSpec(final Collection adapterMethods, - final String adapterClassName) { + protected TypeSpec createTypeSpec(List 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); } + /** + * + * return converter.convert(param, Target.class); + * + */ @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(); } + /** + * + * return converter.convert(param, Target.class, context); + * + */ @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(); } } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/SpringAdapterMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/SpringAdapterMapperGenerator.java index 6dbda0d..877aa96 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/SpringAdapterMapperGenerator.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/SpringAdapterMapperGenerator.java @@ -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 injectAnnotations() { - return CollectionUtil.newArrayList(autowired(), lazy()); + return CollectionUtil.newArrayList(autowired()); } } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AdapterMethodMetadata.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AdapterMethodMetadata.java index 24e1958..bd6f9e5 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AdapterMethodMetadata.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AdapterMethodMetadata.java @@ -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() { diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ClassUtil.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ClassUtil.java new file mode 100644 index 0000000..7aa0d2b --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ClassUtil.java @@ -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(); + } + +} diff --git a/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/SpringConverterFactory.java b/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/SpringConverterFactory.java index e3e91e7..28330d1 100644 --- a/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/SpringConverterFactory.java +++ b/mapstruct-plus-spring-boot-starter/src/main/java/io/github/linpeilie/mapstruct/SpringConverterFactory.java @@ -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; diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/AbstractCachedConverterFactory.java b/mapstruct-plus/src/main/java/io/github/linpeilie/AbstractCachedConverterFactory.java index 84c82dd..0b0e15b 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/AbstractCachedConverterFactory.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/AbstractCachedConverterFactory.java @@ -6,18 +6,20 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory private final ConcurrentHashMap mapperMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap cycleAvoidingMapper = new ConcurrentHashMap<>(); + private final ConcurrentHashMap mapMapperMap = new ConcurrentHashMap<>(); @Override @SuppressWarnings("unchecked") public BaseMapper getMapper(final Class sourceType, final Class 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 BaseCycleAvoidingMapper getCycleAvoidingMapper(Class sourceType, Class targetType) { + BaseMapper mapper = getMapper(sourceType, targetType); + if (mapper == null) { + return null; + } + if (mapper instanceof BaseCycleAvoidingMapper) { + return (BaseCycleAvoidingMapper) mapper; + } + return null; + } + @Override public BaseMapMapper getMapMapper(final Class sourceType) { final Class source = wrapperClass(sourceType); @@ -44,7 +58,7 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory return clazz; } - protected abstract BaseMapper findMapper(final Class source, final Class target); + protected abstract BaseMapper findMapper(Class source, Class target); protected abstract BaseMapMapper findMapMapper(final Class source); diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java index 15785c9..d26ab43 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java @@ -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 extends BaseMapper { @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 convertWithCycle(List sourceList, @Context CycleAvoidingMappingContext context); - - @Override @DoIgnore - default List convert(List sourceList) { - return convertWithCycle(sourceList, new CycleAvoidingMappingContext()); + default List convert(List 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 convert(List sourceList) { + return convert(sourceList, new CycleAvoidingMappingContext()); + } + } diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapMapper.java b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapMapper.java index b40b064..f9ca959 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapMapper.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapMapper.java @@ -6,14 +6,4 @@ public interface BaseMapMapper { T convert(Map map); - default T convertByObj(Object obj) { - if (obj == null) { - return null; - } - if (obj instanceof Map) { - return convert((Map) obj); - } - return null; - } - } diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java index 35147aa..a784b66 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java @@ -16,6 +16,7 @@ public interface BaseMapper { @DoIgnore T convert(S source, @MappingTarget T target); + @DoIgnore default List convert(List sourceList) { if (CollectionUtil.isEmpty(sourceList)) { return new ArrayList<>(); diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/Converter.java b/mapstruct-plus/src/main/java/io/github/linpeilie/Converter.java index 459c22e..92ae3a7 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/Converter.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/Converter.java @@ -48,12 +48,36 @@ public class Converter { } public List convert(List source, Class 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 T convert(S source, Class target, CycleAvoidingMappingContext context) { + if (source == null) { + return null; + } + BaseCycleAvoidingMapper mapper = + (BaseCycleAvoidingMapper) 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 List convert(List source, Class 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 convert(Map map, Class target) { if (map == null || map.isEmpty()) { return null; diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/ConverterFactory.java b/mapstruct-plus/src/main/java/io/github/linpeilie/ConverterFactory.java index 852559c..0242d87 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/ConverterFactory.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/ConverterFactory.java @@ -4,6 +4,8 @@ public interface ConverterFactory { BaseMapper getMapper(Class sourceType, Class targetType); + BaseCycleAvoidingMapper getCycleAvoidingMapper(Class sourceType, Class targetType); + BaseMapMapper getMapMapper(Class sourceType); }