From f78efc39e1a60f388209692fde2dde6ab3864dbf Mon Sep 17 00:00:00 2001 From: linpeilie Date: Mon, 8 Jan 2024 14:12:32 +0800 Subject: [PATCH 01/12] add baidu-site-verification --- docs/.vuepress/config.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index 21710fe..f0a6b10 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -17,6 +17,18 @@ export default defineUserConfig({ description: '' } }, + head: [ + ['script', {}, ` + var _hmt = _hmt || []; + (function() { + var hm = document.createElement("script"); + hm.src = "https://hm.baidu.com/hm.js?fab881821b3db8a7c460db1c91ea0f3a"; + var s = document.getElementsByTagName("script")[0]; + s.parentNode.insertBefore(hm, s); + })(); + `], + ['meta', {name: 'baidu-site-verification', content: 'codeva-OceTRzMGJ2'}], + ], theme: recoTheme(themeConfig), markdown: { anchor: { -- Gitee From 3023bbc5edb6e696dde172deb35ec6ec9f686996 Mon Sep 17 00:00:00 2001 From: gzoldou Date: Mon, 12 Feb 2024 23:15:55 +0800 Subject: [PATCH 02/12] =?UTF-8?q?AutoMapping=E3=80=81ReverseAutoMapping?= =?UTF-8?q?=E6=94=AF=E6=8C=81qualifiedByName=E3=80=81conditionQualifiedByN?= =?UTF-8?q?ame=E5=92=8CdependsOn=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../processor/AutoMapperProcessor.java | 6 ++++ .../generator/AutoMapperGenerator.java | 13 ++++++++ .../metadata/AutoMappingMetadata.java | 30 +++++++++++++++++++ .../linpeilie/annotations/AutoMapping.java | 6 ++++ .../annotations/ReverseAutoMapping.java | 6 ++++ 5 files changed, 61 insertions(+) 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 be2d558..0a2452e 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 @@ -734,6 +734,9 @@ public class AutoMapperProcessor extends AbstractProcessor { metadata.setConditionExpression(reverseAutoMapping.conditionExpression()); metadata.setDateFormat(reverseAutoMapping.dateFormat()); metadata.setNumberFormat(reverseAutoMapping.numberFormat()); + metadata.setQualifiedByName(reverseAutoMapping.qualifiedByName()); + metadata.setConditionQualifiedByName(reverseAutoMapping.conditionQualifiedByName()); + metadata.setDependsOn(reverseAutoMapping.dependsOn()); return metadata; } @@ -808,6 +811,9 @@ public class AutoMapperProcessor extends AbstractProcessor { metadata.setConditionExpression(autoMapping.conditionExpression()); metadata.setDateFormat(autoMapping.dateFormat()); metadata.setNumberFormat(autoMapping.numberFormat()); + metadata.setQualifiedByName(autoMapping.qualifiedByName()); + metadata.setConditionQualifiedByName(autoMapping.conditionQualifiedByName()); + metadata.setDependsOn(autoMapping.dependsOn()); return metadata; } 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 893054b..7bc60b4 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 @@ -1,6 +1,7 @@ package io.github.linpeilie.processor.generator; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; @@ -143,6 +144,18 @@ public class AutoMapperGenerator { if (StringUtils.isNotEmpty(autoMappingMetadata.getConditionExpression())) { builder.addMember("conditionExpression", CodeBlock.builder().add("$S", autoMappingMetadata.getConditionExpression()).build()); } + if (ArrayUtil.isNotEmpty(autoMappingMetadata.getQualifiedByName())) { + builder.addMember("qualifiedByName", CodeBlock.builder().add("$L", + "{" + ArrayUtil.join(autoMappingMetadata.getQualifiedByName(), ",", "\"", "\"") + "}").build()); + } + if (ArrayUtil.isNotEmpty(autoMappingMetadata.getConditionQualifiedByName())) { + builder.addMember("conditionQualifiedByName", CodeBlock.builder().add("$L", + "{" + ArrayUtil.join(autoMappingMetadata.getConditionQualifiedByName(), ",", "\"", "\"") + "}").build()); + } + if (ArrayUtil.isNotEmpty(autoMappingMetadata.getDependsOn())) { + builder.addMember("dependsOn", CodeBlock.builder().add("$L", + "{" + ArrayUtil.join(autoMappingMetadata.getDependsOn(), ",", "\"", "\"") + "}").build()); + } return builder.build(); }).collect(Collectors.toList()); } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMappingMetadata.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMappingMetadata.java index 6ce2531..dbf8944 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMappingMetadata.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMappingMetadata.java @@ -24,6 +24,12 @@ public class AutoMappingMetadata { private String defaultValue = ""; + String[] qualifiedByName = {}; + + String[] conditionQualifiedByName = {}; + + String[] dependsOn = {}; + public ClassName getTargetClass() { return targetClass; } @@ -103,4 +109,28 @@ public class AutoMappingMetadata { public void setConditionExpression(final String conditionExpression) { this.conditionExpression = conditionExpression; } + + public String[] getQualifiedByName() { + return qualifiedByName; + } + + public void setQualifiedByName(String[] qualifiedByName) { + this.qualifiedByName = qualifiedByName; + } + + public String[] getConditionQualifiedByName() { + return conditionQualifiedByName; + } + + public void setConditionQualifiedByName(String[] conditionQualifiedByName) { + this.conditionQualifiedByName = conditionQualifiedByName; + } + + public String[] getDependsOn() { + return dependsOn; + } + + public void setDependsOn(String[] dependsOn) { + this.dependsOn = dependsOn; + } } diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapping.java b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapping.java index 16d8e58..df82176 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapping.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapping.java @@ -40,4 +40,10 @@ public @interface AutoMapping { */ String defaultValue() default ""; + String[] qualifiedByName() default {}; + + String[] conditionQualifiedByName() default {}; + + String[] dependsOn() default {}; + } diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/ReverseAutoMapping.java b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/ReverseAutoMapping.java index ed5c9a4..17cd299 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/ReverseAutoMapping.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/ReverseAutoMapping.java @@ -67,4 +67,10 @@ public @interface ReverseAutoMapping { */ String defaultValue() default ""; + String[] qualifiedByName() default {}; + + String[] conditionQualifiedByName() default {}; + + String[] dependsOn() default {}; + } -- Gitee From 19f163d1745206d44e4f701460aef700165106ed Mon Sep 17 00:00:00 2001 From: linpeilie Date: Thu, 22 Feb 2024 14:10:02 +0800 Subject: [PATCH 03/12] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/pom.xml | 2 +- .../io/github/linpeilie/model/Employee.java | 15 ++++ .../github/linpeilie/model/EmployeeDto.java | 13 ++++ .../io/github/linpeilie/model/Product.java | 21 ++++++ .../io/github/linpeilie/model/ProductDto.java | 21 ++++++ .../linpeilie/model/ProductProperty.java | 18 +++++ .../linpeilie/model/ProductPropertyDto.java | 18 +++++ .../github/linpeilie/EmployeeMapperTest.java | 69 +++++++++++++++++++ .../github/linpeilie/ProductMapperTest.java | 40 +++++++++++ .../AbstractAdapterMapperGenerator.java | 54 ++++++++++++--- .../processor/AutoMapperProcessor.java | 16 ++++- .../generator/AutoMapperGenerator.java | 20 +++--- .../DefaultAdapterMapperGenerator.java | 9 ++- .../generator/IocAdapterMapperGenerator.java | 10 ++- .../AbstractAdapterMethodMetadata.java | 8 +++ .../metadata/AdapterMethodMetadata.java | 13 +++- .../metadata/AutoMapperMetadata.java | 10 +++ .../linpeilie/BaseCycleAvoidingMapper.java | 36 ++++++++++ .../java/io/github/linpeilie/BaseMapper.java | 3 + .../CycleAvoidingMappingContext.java | 23 +++++++ .../linpeilie/annotations/AutoMapper.java | 7 ++ .../linpeilie/annotations/DoIgnore.java | 13 ++++ pom.xml | 2 +- 23 files changed, 415 insertions(+), 26 deletions(-) create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/EmployeeDto.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductDto.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductProperty.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductPropertyDto.java create mode 100644 example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/EmployeeMapperTest.java create mode 100644 example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/ProductMapperTest.java create mode 100644 mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java create mode 100644 mapstruct-plus/src/main/java/io/github/linpeilie/CycleAvoidingMappingContext.java create mode 100644 mapstruct-plus/src/main/java/io/github/linpeilie/annotations/DoIgnore.java diff --git a/example/pom.xml b/example/pom.xml index 0a89080..1140c43 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -18,7 +18,7 @@ UTF-8 1.5.1.Final - 1.3.6 + 1.3.7-SNAPSHOTS 1.18.22 diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee.java new file mode 100644 index 0000000..92d4546 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee.java @@ -0,0 +1,15 @@ +package io.github.linpeilie.model; + +import io.github.linpeilie.annotations.AutoMapper; +import java.util.List; +import lombok.Data; + +@Data +@AutoMapper(target = EmployeeDto.class, cycles = true) +public class Employee { + + private String name; + private Employee reportsTo; + private List team; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/EmployeeDto.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/EmployeeDto.java new file mode 100644 index 0000000..1bf2baf --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/EmployeeDto.java @@ -0,0 +1,13 @@ +package io.github.linpeilie.model; + +import java.util.List; +import lombok.Data; + +@Data +public class EmployeeDto { + + private String employeeName; + private EmployeeDto reportsTo; + private List team; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java new file mode 100644 index 0000000..590005a --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java @@ -0,0 +1,21 @@ +package io.github.linpeilie.model; + +import io.github.linpeilie.annotations.AutoMapper; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@AutoMapper(target = ProductDto.class, cycles = true) +public class Product { + + private Long id; + + private String name; + + @ToString.Exclude + @EqualsAndHashCode.Exclude + private List productPropertyList; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductDto.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductDto.java new file mode 100644 index 0000000..53c4051 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductDto.java @@ -0,0 +1,21 @@ +package io.github.linpeilie.model; + +import io.github.linpeilie.annotations.AutoMapper; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@AutoMapper(target = Product.class, cycles = true) +public class ProductDto { + + private Long id; + + private String name; + + @ToString.Exclude + @EqualsAndHashCode.Exclude + private List productPropertyList; + +} 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 new file mode 100644 index 0000000..a297e64 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductProperty.java @@ -0,0 +1,18 @@ +package io.github.linpeilie.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +public class ProductProperty { + + private Long id; + + private String name; + + @ToString.Exclude + @EqualsAndHashCode.Exclude + private Product product; + +} 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 new file mode 100644 index 0000000..f6a90ca --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductPropertyDto.java @@ -0,0 +1,18 @@ +package io.github.linpeilie.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +public class ProductPropertyDto { + + private Long id; + + private String name; + + @ToString.Exclude + @EqualsAndHashCode.Exclude + private ProductDto product; + +} diff --git a/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/EmployeeMapperTest.java b/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/EmployeeMapperTest.java new file mode 100644 index 0000000..2d9ca14 --- /dev/null +++ b/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/EmployeeMapperTest.java @@ -0,0 +1,69 @@ +package io.github.linpeilie; + +import io.github.linpeilie.model.Employee; +import io.github.linpeilie.model.EmployeeDto; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +public class EmployeeMapperTest { + + @Autowired + private Converter converter; + + @Test + public void testMapDtoToEntity() { + + EmployeeDto teamLeader = employeeDto("Group Leader", null); + + EmployeeDto member1 = employeeDto("Member2", teamLeader); + EmployeeDto member2 = employeeDto("Member2", teamLeader); + teamLeader.setTeam(Arrays.asList(member1, member2)); + + Employee teamLead = converter.convert(teamLeader, Employee.class); + + assertThat(teamLead).isNotNull(); + assertThat(teamLead.getReportsTo()).isNull(); + List team = teamLead.getTeam(); + assertThat(team).hasSize(2); + assertThat(team).extracting("reportsTo").containsExactly(teamLead, teamLead); + } + + private EmployeeDto employeeDto(String name, EmployeeDto reportsTo) { + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setEmployeeName(name); + employeeDto.setReportsTo(reportsTo); + return employeeDto; + } + + @Test + public void testMapEntityToDto() { + + Employee teamLeader = employee("Group Leader", null); + + Employee member1 = employee("Member2", teamLeader); + Employee member2 = employee("Member2", teamLeader); + teamLeader.setTeam(Arrays.asList(member1, member2)); + + EmployeeDto teamLead = converter.convert(teamLeader, EmployeeDto.class); + + assertThat(teamLead).isNotNull(); + assertThat(teamLead.getReportsTo()).isNull(); + List team = teamLead.getTeam(); + assertThat(team).hasSize(2); + assertThat(team).extracting("reportsTo").containsExactly(teamLead, teamLead); + } + + private Employee employee(String name, Employee reportsTo) { + Employee employee = new Employee(); + employee.setName(name); + employee.setReportsTo(reportsTo); + return employee; + } + +} diff --git a/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/ProductMapperTest.java b/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/ProductMapperTest.java new file mode 100644 index 0000000..209ae67 --- /dev/null +++ b/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/ProductMapperTest.java @@ -0,0 +1,40 @@ +package io.github.linpeilie; + +import io.github.linpeilie.model.Product; +import io.github.linpeilie.model.ProductDto; +import io.github.linpeilie.model.ProductProperty; +import java.util.Arrays; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class ProductMapperTest { + + @Autowired + private Converter converter; + + @Test + public void test() { + Product product = new Product(); + product.setId(1L); + product.setName("Product"); + + ProductProperty productProperty1 = new ProductProperty(); + productProperty1.setId(1L); + productProperty1.setName("ProductProperty"); + productProperty1.setProduct(product); + + ProductProperty productProperty2 = new ProductProperty(); + productProperty2.setId(1L); + productProperty2.setName("ProductProperty"); + productProperty2.setProduct(product); + + product.setProductPropertyList(Arrays.asList(productProperty1, productProperty2)); + + ProductDto productDto = converter.convert(product, ProductDto.class); + + System.out.println(productDto); + } + +} 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 889ff3e..10b4c32 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 @@ -11,7 +11,10 @@ import com.squareup.javapoet.TypeSpec; import io.github.linpeilie.processor.metadata.AbstractAdapterMethodMetadata; 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 javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -48,22 +51,55 @@ public abstract class AbstractAdapterMapperGenerator { return source; } if ("java.util.Map".contentEquals(source.toString())) { - return ParameterizedTypeName.get((ClassName) source, - ClassName.get("java.lang", "String"), + return ParameterizedTypeName.get((ClassName) source, ClassName.get("java.lang", "String"), ClassName.get("java.lang", "Object")); } return source; } - protected MethodSpec buildProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) { - 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(); + protected List buildProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) { + List methodSpecs = new ArrayList<>(); + + if (adapterMethodMetadata.needCycleAvoiding()) { + methodSpecs.add(buildCycleAvoidingProxyMethod(adapterMethodMetadata)); + } else { + methodSpecs.add(buildDefaultProxyMethod(adapterMethodMetadata)); + } + + return methodSpecs; + } + + protected MethodSpec buildDefaultProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) { + 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()) + .addModifiers(Modifier.PUBLIC) + .addParameter(parameterSpec) + .returns(adapterMethodMetadata.getReturn()) + .addCode(targetCode) + .build(); + } + + protected MethodSpec buildCycleAvoidingProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata) { + CodeBlock targetCode = adapterMethodMetadata.isStatic() + ? CodeBlock.of("return $T.$N($N, $N);", adapterMethodMetadata.getMapper(), + adapterMethodMetadata.getMapperMethodName(), "param", "context") + : cycleAvoidingMethodTarget(adapterMethodMetadata); + ParameterSpec parameterSpec = + ParameterSpec.builder(wrapperTypeName(adapterMethodMetadata.getSource()), "param").build(); + ParameterSpec contextParameterSpec = + ParameterSpec.builder( + ClassName.get("io.github.linpeilie", "CycleAvoidingMappingContext"), + "context") + .build(); return MethodSpec.methodBuilder(adapterMethodMetadata.getMethodName()) .addModifiers(Modifier.PUBLIC) .addParameter(parameterSpec) + .addParameter(contextParameterSpec) .returns(adapterMethodMetadata.getReturn()) .addCode(targetCode) .build(); @@ -71,4 +107,6 @@ public abstract class AbstractAdapterMapperGenerator { protected abstract CodeBlock proxyMethodTarget(AbstractAdapterMethodMetadata adapterMethodMetadata); + protected abstract CodeBlock cycleAvoidingMethodTarget(AbstractAdapterMethodMetadata adapterMethodMetadata); + } 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 be2d558..3c8d581 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 @@ -540,6 +540,12 @@ public class AutoMapperProcessor extends AbstractProcessor { 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")); + } else { + reverseMapperMetadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseMapper")); + } if (CollectionUtil.isNotEmpty(autoMapperMetadata.getFieldReverseMappingList())) { reverseMapperMetadata.setFieldMappingList(autoMapperMetadata.getFieldReverseMappingList()); } else { @@ -575,7 +581,8 @@ public class AutoMapperProcessor extends AbstractProcessor { return; } AdapterMethodMetadata adapterMethodMetadata = AdapterMethodMetadata.newInstance( - metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass()); + metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass(), + metadata.isCycles()); methodMap.putIfAbsent(adapterMethodMetadata.getMethodName(), adapterMethodMetadata); } @@ -590,7 +597,6 @@ public class AutoMapperProcessor extends AbstractProcessor { metadata.setSourceClassName(source); metadata.setTargetClassName(target); - metadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseMapper")); metadata.setSuperGenerics(new ClassName[] {source, target}); metadata.setMapstructConfigClass( ClassName.get(AutoMapperProperties.getConfigPackage(), AutoMapperProperties.getConfigClassName())); @@ -672,6 +678,12 @@ public class AutoMapperProcessor extends AbstractProcessor { metadata.setFieldReverseMappingList(reverseMappingMetadataList); metadata.setConvertGenerate(autoMapper.convertGenerate()); metadata.setReverseConvertGenerate(autoMapper.reverseConvertGenerate()); + metadata.setCycles(autoMapper.cycles()); + if (metadata.isCycles()) { + metadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseCycleAvoidingMapper")); + } else { + metadata.setSuperClass(ClassName.get("io.github.linpeilie", "BaseMapper")); + } addMapper(metadata); 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 893054b..17b3c12 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,7 +10,6 @@ import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeSpec; -import io.github.linpeilie.annotations.Immutable; import io.github.linpeilie.processor.metadata.AutoMapperMetadata; import io.github.linpeilie.processor.metadata.AutoMappingMetadata; import java.io.IOException; @@ -26,7 +25,6 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; -import javax.tools.Diagnostic; import org.apache.commons.lang3.StringUtils; import static io.github.linpeilie.processor.Constants.*; @@ -35,6 +33,8 @@ 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); @@ -63,15 +63,15 @@ public class AutoMapperGenerator { .build(); if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) { builder.addMethod(addConvertMethodSpec(Collections.singletonList(source), metadata.getFieldMappingList(), - targetClassName)); + targetClassName, metadata.isCycles())); } boolean targetIsImmutable = classIsImmutable(processingEnv, targetClassName); if (targetIsImmutable) { - builder.addMethod(addEmptyConvertMethodForImmutableEntity(source, target, targetClassName)); + builder.addMethod(addEmptyConvertMethodForImmutableEntity(source, target, targetClassName, metadata.isCycles())); } else { builder.addMethod(addConvertMethodSpec(Arrays.asList(source, target), metadata.getFieldMappingList(), - targetClassName)); + targetClassName, metadata.isCycles())); } return builder.build(); @@ -79,8 +79,9 @@ public class AutoMapperGenerator { private MethodSpec addEmptyConvertMethodForImmutableEntity(ParameterSpec source, ParameterSpec target, - ClassName targetClassName) { - return MethodSpec.methodBuilder(CONVERT_METHOD_NAME) + ClassName targetClassName, boolean cycles) { + String methodName = cycles ? CONVERT_WITH_CYCLE_METHOD_NAME : CONVERT_METHOD_NAME; + return MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT) .addParameter(source) .addParameter(target) @@ -103,8 +104,9 @@ public class AutoMapperGenerator { private MethodSpec addConvertMethodSpec(List parameterSpecs, List autoMappingMetadataList, - ClassName target) { - final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(CONVERT_METHOD_NAME) + ClassName target, boolean cycles) { + String methodName = cycles ? CONVERT_WITH_CYCLE_METHOD_NAME : CONVERT_METHOD_NAME; + final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodName) .addParameters(parameterSpecs) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .returns(target); 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 3caaa0f..3ef885c 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 @@ -17,7 +17,7 @@ public class DefaultAdapterMapperGenerator extends AbstractAdapterMapperGenerato ClassName.get(adapterPackage(), adapterClassName)) .addModifiers(Modifier.PUBLIC); - adapterMethods.forEach(adapterMethod -> adapterBuilder.addMethod(buildProxyMethod(adapterMethod))); + adapterMethods.forEach(adapterMethod -> adapterBuilder.addMethods(buildProxyMethod(adapterMethod))); return adapterBuilder.build(); } @@ -32,4 +32,11 @@ public class DefaultAdapterMapperGenerator extends AbstractAdapterMapperGenerato ClassName.get("org.mapstruct.factory", "Mappers"), adapterMethodMetadata.getMapper(), adapterMethodMetadata.getMapperMethodName(), "param"); } + + @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"); + } } 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 0de2aad..d880a3d 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 @@ -31,7 +31,7 @@ public abstract class IocAdapterMapperGenerator extends AbstractAdapterMapperGen .forEach(mapper -> adapterBuilder.addField(buildMapperField(mapper))); adapterMethods.forEach(adapterMethod -> adapterBuilder - .addMethod(buildProxyMethod(adapterMethod))); + .addMethods(buildProxyMethod(adapterMethod))); return adapterBuilder.build(); } @@ -55,4 +55,12 @@ public abstract class IocAdapterMapperGenerator extends AbstractAdapterMapperGen .build(); } + @Override + protected CodeBlock cycleAvoidingMethodTarget(AbstractAdapterMethodMetadata adapterMethodMetadata) { + return CodeBlock.builder() + .add("return $N.$N($N, $N);", firstWordToLower(adapterMethodMetadata.getMapper().simpleName()), + adapterMethodMetadata.cycleAvoidingMethodName(), + "param", "context") + .build(); + } } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AbstractAdapterMethodMetadata.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AbstractAdapterMethodMetadata.java index 1c46378..ec5de7a 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AbstractAdapterMethodMetadata.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AbstractAdapterMethodMetadata.java @@ -32,4 +32,12 @@ public abstract class AbstractAdapterMethodMetadata { return false; } + public boolean needCycleAvoiding() { + return false; + } + + public String cycleAvoidingMethodName() { + return "convertWithCycle"; + } + } 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 83f0a2c..24e1958 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 @@ -4,15 +4,17 @@ import com.squareup.javapoet.ClassName; public class AdapterMethodMetadata extends AbstractAdapterMethodMetadata { - private AdapterMethodMetadata(final ClassName source, final ClassName target, ClassName mapper) { + private AdapterMethodMetadata(final ClassName source, final ClassName target, ClassName mapper, boolean needCycleAvoiding) { super(source, mapper); this.target = target; + this.needCycleAvoiding = needCycleAvoiding; } private final ClassName target; + private final boolean needCycleAvoiding; - public static AdapterMethodMetadata newInstance(ClassName source, ClassName target, ClassName mapper) { - return new AdapterMethodMetadata(source, target, mapper); + public static AdapterMethodMetadata newInstance(ClassName source, ClassName target, ClassName mapper, boolean needCycleAvoiding) { + return new AdapterMethodMetadata(source, target, mapper, needCycleAvoiding); } @Override @@ -35,4 +37,9 @@ public class AdapterMethodMetadata extends AbstractAdapterMethodMetadata { public String getMapperMethodName() { return "convert"; } + + @Override + public boolean needCycleAvoiding() { + return needCycleAvoiding; + } } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java index 6f4d58b..dc1a598 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java @@ -27,6 +27,8 @@ public class AutoMapperMetadata extends AbstractMapperMetadata { private boolean reverseConvertGenerate; + private boolean cycles; + public String mapperName() { return sourceClassName.simpleName() + "To" + targetClassName.simpleName() + "Mapper"; } @@ -113,4 +115,12 @@ public class AutoMapperMetadata extends AbstractMapperMetadata { public void setConvertGenerate(final boolean convertGenerate) { this.convertGenerate = convertGenerate; } + + public boolean isCycles() { + return cycles; + } + + public void setCycles(boolean cycles) { + this.cycles = cycles; + } } diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java new file mode 100644 index 0000000..15785c9 --- /dev/null +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java @@ -0,0 +1,36 @@ +package io.github.linpeilie; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.linpeilie.annotations.DoIgnore; +import java.util.List; +import org.mapstruct.Context; +import org.mapstruct.MappingTarget; + +public interface BaseCycleAvoidingMapper extends BaseMapper { + + @DoIgnore + T convertWithCycle(S source, @Context CycleAvoidingMappingContext context); + + @DoIgnore + T convertWithCycle(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()); + } + + @Override + @DoIgnore + default T convert(S source) { + return convertWithCycle(source, new CycleAvoidingMappingContext()); + } + + @Override + @DoIgnore + default T convert(S source, T target) { + return convertWithCycle(source, new CycleAvoidingMappingContext()); + } +} 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 e031b5a..35147aa 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java @@ -1,6 +1,7 @@ package io.github.linpeilie; import cn.hutool.core.collection.CollectionUtil; +import io.github.linpeilie.annotations.DoIgnore; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -9,8 +10,10 @@ import org.mapstruct.MappingTarget; public interface BaseMapper { + @DoIgnore T convert(S source); + @DoIgnore T convert(S source, @MappingTarget T target); default List convert(List sourceList) { diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/CycleAvoidingMappingContext.java b/mapstruct-plus/src/main/java/io/github/linpeilie/CycleAvoidingMappingContext.java new file mode 100644 index 0000000..edd82af --- /dev/null +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/CycleAvoidingMappingContext.java @@ -0,0 +1,23 @@ +package io.github.linpeilie; + +import java.util.IdentityHashMap; +import java.util.Map; +import org.mapstruct.BeforeMapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.TargetType; + +public class CycleAvoidingMappingContext { + + private Map knownInstances = new IdentityHashMap(); + + @BeforeMapping + public T getMappedInstance(Object source, @TargetType Class targetType) { + return (T) knownInstances.get( source ); + } + + @BeforeMapping + public void storeMappedInstance(Object source, @MappingTarget Object target) { + knownInstances.put( source, target ); + } + +} diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapper.java b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapper.java index d216afd..8beae7b 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapper.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapper.java @@ -32,4 +32,11 @@ public @interface AutoMapper { */ boolean reverseConvertGenerate() default true; + /** + * 是否有循环依赖对象 + * + * @return true: 有循环依赖对象 false: 没有循环依赖对象 + */ + boolean cycles() default false; + } diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/DoIgnore.java b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/DoIgnore.java new file mode 100644 index 0000000..abd5ecf --- /dev/null +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/DoIgnore.java @@ -0,0 +1,13 @@ +package io.github.linpeilie.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.mapstruct.Qualifier; + +@Qualifier +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface DoIgnore { +} diff --git a/pom.xml b/pom.xml index c908be3..ede6ffd 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 1.3.6 + 1.3.7-SNAPSHOTS 8 8 UTF-8 -- Gitee From 4e103c7cd1ceb5c283a3beef8ce0c19b5a871b9f Mon Sep 17 00:00:00 2001 From: linpeilie Date: Tue, 12 Mar 2024 10:39:27 +0800 Subject: [PATCH 04/12] =?UTF-8?q?-=20=E9=80=82=E9=85=8D=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E5=B5=8C=E5=A5=97=20-=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/github/linpeilie/model/Dept.java | 19 ++ .../io/github/linpeilie/model/DeptDTO.java | 17 ++ .../io/github/linpeilie/model/Employee1.java | 12 + .../github/linpeilie/model/Employee1Dto.java | 12 + .../linpeilie/model/ProductProperty.java | 2 + .../linpeilie/model/ProductPropertyDto.java | 2 + .../AbstractAdapterMapperGenerator.java | 182 ++++++++++++-- .../processor/AutoMapperProcessor.java | 236 +++++++++--------- .../processor/AutoMapperProperties.java | 27 +- .../linpeilie/processor/BuildCollator.java | 2 +- .../github/linpeilie/processor/Constants.java | 52 ---- .../linpeilie/processor/ContextConstants.java | 107 ++++++++ .../generator/AutoMapperGenerator.java | 68 ++--- .../DefaultAdapterMapperGenerator.java | 18 +- .../generator/IocAdapterMapperGenerator.java | 52 ++-- .../SpringAdapterMapperGenerator.java | 8 +- .../metadata/AdapterMethodMetadata.java | 7 +- .../linpeilie/processor/utils/ClassUtil.java | 24 ++ .../mapstruct/SpringConverterFactory.java | 1 + .../AbstractCachedConverterFactory.java | 24 +- .../linpeilie/BaseCycleAvoidingMapper.java | 26 +- .../io/github/linpeilie/BaseMapMapper.java | 10 - .../java/io/github/linpeilie/BaseMapper.java | 1 + .../java/io/github/linpeilie/Converter.java | 26 +- .../io/github/linpeilie/ConverterFactory.java | 2 + 25 files changed, 631 insertions(+), 306 deletions(-) create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Dept.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/DeptDTO.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee1.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee1Dto.java delete mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/Constants.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/ContextConstants.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ClassUtil.java 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 (adapterMethodMetadata.needCycleAvoiding()) { - methodSpecs.add(buildCycleAvoidingProxyMethod(adapterMethodMetadata)); + if (cycle) { + methodSpecs.addAll(buildCycleAvoidingProxyMethod(adapterMethod)); } else { - methodSpecs.add(buildDefaultProxyMethod(adapterMethodMetadata)); + methodSpecs.addAll(buildDefaultProxyMethod(adapterMethod, null)); + } + return methodSpecs; + } + + protected List buildDefaultProxyMethod(AbstractAdapterMethodMetadata adapterMethodMetadata, + ClassName annotation) { + List methodSpecs = new ArrayList<>(); + + 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 = ContextConstants.ConvertAdapter.convertMapperAdapterClassName; - private static String adapterClassName = DEFAULT_ADAPTER_CLASS_NAME; + private static String mapAdapterClassName = ContextConstants.ConvertAdapter.mapConvertMapperAdapterClassName; - private static String mapAdapterClassName = DEFAULT_MAP_ADAPTER_CLASS_NAME; + private static String autoConfigPackage = ContextConstants.AutoConfig.packageName; - private static String autoConfigPackage = DEFAULT_BASE_PACKAGE; + private static String autoMapperConfigClassName = ContextConstants.AutoConfig.autoMapperConfigClassName; - private static String autoMapperConfigClassName = AUTO_MAPPER_CONFIG_CLASS_NAME; + private static String autoMapMapperConfigClassName = ContextConstants.AutoConfig.autoMapMapperConfigClassName; - private static String autoMapMapperConfigClassName = AUTO_MAP_MAPPER_CONFIG_CLASS_NAME; + /* ******************** 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()); + + adapterBuilder.addMethods(methods); - adapterMethods.forEach(adapterMethod -> adapterBuilder - .addMethods(buildProxyMethod(adapterMethod))); + 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); + @DoIgnore + default List convert(List sourceList, @Context CycleAvoidingMappingContext context) { + return sourceList.stream() + .map(item -> convert(item, context)) + .collect(Collectors.toList()); + } @Override @DoIgnore - default List convert(List sourceList) { - return convertWithCycle(sourceList, new CycleAvoidingMappingContext()); + default T convert(S source) { + return convert(source, new CycleAvoidingMappingContext()); } @Override @DoIgnore - default T convert(S source) { - return convertWithCycle(source, new CycleAvoidingMappingContext()); + default T convert(S source, @MappingTarget T target) { + return convert(source, new CycleAvoidingMappingContext()); } + @Override @DoIgnore - default T convert(S source, T target) { - return convertWithCycle(source, new CycleAvoidingMappingContext()); + 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); } -- Gitee From abe54d84cc2b74b519bc40224d80ea9791af0220 Mon Sep 17 00:00:00 2001 From: linpeilie Date: Tue, 12 Mar 2024 11:49:35 +0800 Subject: [PATCH 05/12] =?UTF-8?q?AutoMappings=E6=B3=A8=E8=A7=A3=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE=E5=9C=A8=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E4=B8=8A=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/github/linpeilie/annotations/AutoMappings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMappings.java b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMappings.java index b88fc06..d8f0cb6 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMappings.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMappings.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.FIELD) +@Target({ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.CLASS) public @interface AutoMappings { -- Gitee From 4b97f2bfbdbbaa6d1960e96471f0da04621474af Mon Sep 17 00:00:00 2001 From: linpeilie Date: Tue, 12 Mar 2024 16:14:01 +0800 Subject: [PATCH 06/12] =?UTF-8?q?=E5=8E=BB=E9=99=A4hutool=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=EF=BC=8C=E7=94=A8=E6=88=B7=E5=8F=AF=E4=BB=A5=E6=8C=89?= =?UTF-8?q?=E9=9C=80=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/pom.xml | 6 + example/quick-start/pom.xml | 4 + example/spring-boot-with-lombok/pom.xml | 4 + example/spring-boot/pom.xml | 4 + .../io/github/linpeilie/QuickStartTest.java | 9 +- mapstruct-plus-object-convert/pom.xml | 30 ++++ .../linpeilie/map/MapObjectConvert.java | 0 mapstruct-plus-processor/pom.xml | 4 - .../processor/AutoMapperProcessor.java | 20 +-- .../linpeilie/processor/BuildCollator.java | 14 +- .../generator/AutoMapperGenerator.java | 24 ++- .../generator/MapperConfigGenerator.java | 5 +- .../SolonAdapterMapperGenerator.java | 4 +- .../SpringAdapterMapperGenerator.java | 13 +- .../metadata/AbstractMapperMetadata.java | 4 +- .../metadata/AutoMapMapperMetadata.java | 6 +- .../solon/SolonComponentProcessor.java | 7 +- .../processor/utils/ExceptionUtils.java | 15 ++ .../linpeilie/processor/utils/FileReader.java | 61 ++++++++ .../linpeilie/processor/utils/FileUtils.java | 145 ++++++++++++++++++ .../processor/utils/FileWrapper.java | 68 ++++++++ .../linpeilie/processor/utils/FileWriter.java | 89 +++++++++++ .../processor/utils/LineSeparator.java | 20 +++ .../processor/utils/ObjectUtils.java | 9 ++ .../processor/utils/ThreadUtils.java | 14 ++ mapstruct-plus/pom.xml | 4 +- .../linpeilie/BaseCycleAvoidingMapper.java | 1 - .../java/io/github/linpeilie/BaseMapper.java | 5 +- .../io/github/linpeilie/utils/CharUtils.java | 41 +++++ .../linpeilie/utils/CollectionUtils.java | 29 ++++ .../io/github/linpeilie/utils/StrUtil.java | 86 +++++++++++ pom.xml | 13 +- 32 files changed, 682 insertions(+), 76 deletions(-) create mode 100644 mapstruct-plus-object-convert/pom.xml rename {mapstruct-plus => mapstruct-plus-object-convert}/src/main/java/io/github/linpeilie/map/MapObjectConvert.java (100%) create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ExceptionUtils.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileReader.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileUtils.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileWrapper.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileWriter.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/LineSeparator.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ObjectUtils.java create mode 100644 mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ThreadUtils.java create mode 100644 mapstruct-plus/src/main/java/io/github/linpeilie/utils/CharUtils.java create mode 100644 mapstruct-plus/src/main/java/io/github/linpeilie/utils/CollectionUtils.java create mode 100644 mapstruct-plus/src/main/java/io/github/linpeilie/utils/StrUtil.java diff --git a/example/pom.xml b/example/pom.xml index 1140c43..4c9dbbd 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -20,6 +20,7 @@ 1.5.1.Final 1.3.7-SNAPSHOTS 1.18.22 + 5.8.26 @@ -44,6 +45,11 @@ lombok ${lombok.version} + + cn.hutool + hutool-all + ${hutool.version} + diff --git a/example/quick-start/pom.xml b/example/quick-start/pom.xml index 323f2a0..b3f6f4c 100644 --- a/example/quick-start/pom.xml +++ b/example/quick-start/pom.xml @@ -28,6 +28,10 @@ 5.9.2 test + + cn.hutool + hutool-all + diff --git a/example/spring-boot-with-lombok/pom.xml b/example/spring-boot-with-lombok/pom.xml index 2ef8f86..81a92c8 100644 --- a/example/spring-boot-with-lombok/pom.xml +++ b/example/spring-boot-with-lombok/pom.xml @@ -46,6 +46,10 @@ org.projectlombok lombok + + cn.hutool + hutool-all + diff --git a/example/spring-boot/pom.xml b/example/spring-boot/pom.xml index 428f4ae..a97857c 100644 --- a/example/spring-boot/pom.xml +++ b/example/spring-boot/pom.xml @@ -42,6 +42,10 @@ io.github.linpeilie mapstruct-plus-spring-boot-starter + + cn.hutool + hutool-all + diff --git a/example/spring-boot/src/test/java/io/github/linpeilie/QuickStartTest.java b/example/spring-boot/src/test/java/io/github/linpeilie/QuickStartTest.java index 596685d..e8a89d5 100644 --- a/example/spring-boot/src/test/java/io/github/linpeilie/QuickStartTest.java +++ b/example/spring-boot/src/test/java/io/github/linpeilie/QuickStartTest.java @@ -1,6 +1,5 @@ package io.github.linpeilie; -import cn.hutool.core.date.DateUtil; import io.github.linpeilie.model.Goods; import io.github.linpeilie.model.GoodsDto; import io.github.linpeilie.model.MapModelA; @@ -27,14 +26,14 @@ public class QuickStartTest { private Converter converter; @Test - public void test() { + public void test() throws ParseException { Map mapModel1 = new HashMap<>(); mapModel1.put("str", "1jkf1ijkj3f"); mapModel1.put("i1", 111); mapModel1.put("l2", 11231); Map mapModel2 = new HashMap<>(); - mapModel2.put("date", DateUtil.parse("2023-02-23 01:03:23")); + mapModel2.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2023-02-23 01:03:23")); mapModel1.put("mapModelB", mapModel2); @@ -99,7 +98,7 @@ public class QuickStartTest { } @Test - public void multiClassConvertTest() { + public void multiClassConvertTest() throws ParseException { User user = new User(); List list = new ArrayList<>(); list.add("1"); @@ -110,7 +109,7 @@ public class QuickStartTest { user.setUsername("Nick"); user.setAge(12); user.setYoung(true); - user.setBirthday(DateUtil.parseDateTime("2023-02-23 02:01:43")); + user.setBirthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2023-02-23 02:01:43")); user.setAssets(123.234); user.setVoField("vofieldfff"); user.setMoney(12543.123); diff --git a/mapstruct-plus-object-convert/pom.xml b/mapstruct-plus-object-convert/pom.xml new file mode 100644 index 0000000..d931c4b --- /dev/null +++ b/mapstruct-plus-object-convert/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + io.github.linpeilie + mapstruct-plus-pom + ${mapstruct-plus.version} + ../pom.xml + + + mapstruct-plus-object-convert + + + 8 + 8 + UTF-8 + + + + + cn.hutool + hutool-core + true + provided + + + + \ No newline at end of file diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/map/MapObjectConvert.java b/mapstruct-plus-object-convert/src/main/java/io/github/linpeilie/map/MapObjectConvert.java similarity index 100% rename from mapstruct-plus/src/main/java/io/github/linpeilie/map/MapObjectConvert.java rename to mapstruct-plus-object-convert/src/main/java/io/github/linpeilie/map/MapObjectConvert.java diff --git a/mapstruct-plus-processor/pom.xml b/mapstruct-plus-processor/pom.xml index bd6dcdf..e11702d 100644 --- a/mapstruct-plus-processor/pom.xml +++ b/mapstruct-plus-processor/pom.xml @@ -27,10 +27,6 @@ com.baidu.lbsyun javapoet - - org.apache.commons - commons-lang3 - org.mapstruct mapstruct-processor 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 dd89a7a..78986c8 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 @@ -1,9 +1,5 @@ package io.github.linpeilie.processor; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeName; import io.github.linpeilie.ComponentModelConstant; @@ -31,6 +27,10 @@ import io.github.linpeilie.processor.metadata.AutoEnumMapperMetadata; import io.github.linpeilie.processor.metadata.AutoMapMapperMetadata; import io.github.linpeilie.processor.metadata.AutoMapperMetadata; import io.github.linpeilie.processor.metadata.AutoMappingMetadata; +import io.github.linpeilie.processor.utils.ExceptionUtils; +import io.github.linpeilie.processor.utils.ObjectUtils; +import io.github.linpeilie.utils.CollectionUtils; +import io.github.linpeilie.utils.StrUtil; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; @@ -149,7 +149,7 @@ public class AutoMapperProcessor extends AbstractProcessor { try { doProcess(annotations, roundEnv); } catch (Exception e) { - messager.printMessage(ERROR, ExceptionUtil.stacktraceToString(e)); + messager.printMessage(ERROR, ExceptionUtils.getStackTrace(e)); } return false; @@ -398,7 +398,7 @@ public class AutoMapperProcessor extends AbstractProcessor { // load previous mapper config final List typeElements = buildCollator.getRecords(); - if (CollectionUtil.isNotEmpty(typeElements)) { + if (CollectionUtils.isNotEmpty(typeElements)) { messager.printMessage(Diagnostic.Kind.NOTE, "The previous Mapper Config Class was read , class name : " + typeElements.get(0)); loadMapperConfig(typeElements.get(0).getAnnotation(MapperConfig.class)); @@ -413,7 +413,7 @@ public class AutoMapperProcessor extends AbstractProcessor { if (mapperConfigOptional.isPresent()) { loadMapperConfig(mapperConfigOptional.get().getAnnotation(MapperConfig.class)); // record - buildCollator.writeTypeElements(CollectionUtil.newArrayList((TypeElement) mapperConfigOptional.get())); + buildCollator.writeTypeElements(Collections.singletonList((TypeElement) mapperConfigOptional.get())); } } @@ -483,7 +483,7 @@ public class AutoMapperProcessor extends AbstractProcessor { if (!autoMapperMetadata.isReverseConvertGenerate()) { return; } - boolean defineReverseMapping = CollectionUtil.isNotEmpty(autoMapperMetadata.getFieldReverseMappingList()); + boolean defineReverseMapping = CollectionUtils.isNotEmpty(autoMapperMetadata.getFieldReverseMappingList()); final AutoMapperMetadata reverseMapperMetadata = reverseMapper(autoMapperMetadata); if (defineReverseMapping) { addMapper(reverseMapperMetadata); @@ -544,7 +544,7 @@ public class AutoMapperProcessor extends AbstractProcessor { reverseMapperMetadata.setSuperClass( ClassName.get(ContextConstants.BaseMapper.packageName, ContextConstants.BaseMapper.className)); } - if (CollectionUtil.isNotEmpty(autoMapperMetadata.getFieldReverseMappingList())) { + if (CollectionUtils.isNotEmpty(autoMapperMetadata.getFieldReverseMappingList())) { reverseMapperMetadata.setFieldMappingList(autoMapperMetadata.getFieldReverseMappingList()); } else { // 需要继承的属性 @@ -799,7 +799,7 @@ public class AutoMapperProcessor extends AbstractProcessor { String elementName = ele.getSimpleName().toString(); if (ele.getKind() == ElementKind.METHOD) { - elementName = ObjectUtil.defaultIfBlank(StrUtil.getGeneralField(elementName), elementName); + elementName = ObjectUtils.defaultIfNull(StrUtil.getGeneralField(elementName), elementName); } if (StrUtil.isNotEmpty(autoMapping.source())) { 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 a47aaa1..03c0cb5 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 @@ -1,7 +1,7 @@ package io.github.linpeilie.processor; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.io.FileUtil; +import io.github.linpeilie.processor.utils.FileUtils; +import io.github.linpeilie.utils.CollectionUtils; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -41,7 +41,7 @@ public class BuildCollator { ContextConstants.MetaInf.folder + fileName); this.collatorFile = new File(fileObject.getName()); if (collatorFile.exists()) { - records = FileUtil.readUtf8Lines(collatorFile) + records = FileUtils.readUtf8Lines(collatorFile) .stream() .map(line -> processingEnv.getElementUtils().getTypeElement(line)) .filter(Objects::nonNull) @@ -62,7 +62,7 @@ public class BuildCollator { } private void write(Collection lines) { - if (CollectionUtil.isEmpty(lines)) { + if (CollectionUtils.isEmpty(lines)) { return; } @@ -71,8 +71,8 @@ public class BuildCollator { .filter(Objects::nonNull) .collect(Collectors.toList()); - FileUtil.mkParentDirs(collatorFile); - FileUtil.writeUtf8Lines(lines, collatorFile); + FileUtils.mkParentDirs(collatorFile); + FileUtils.writeUtf8Lines(lines, collatorFile); } private List loadRecords() { @@ -87,7 +87,7 @@ public class BuildCollator { public void consumeRecords(Consumer consumer) { final List typeElements = getRecords(); - if (CollectionUtil.isNotEmpty(typeElements)) { + if (CollectionUtils.isNotEmpty(typeElements)) { typeElements.forEach(consumer); } } 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 5d5378c..9dda68c 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 @@ -1,7 +1,5 @@ package io.github.linpeilie.processor.generator; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.StrUtil; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; @@ -13,6 +11,8 @@ 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 io.github.linpeilie.utils.CollectionUtils; +import io.github.linpeilie.utils.StrUtil; import java.io.IOException; import java.io.UncheckedIOException; import java.io.Writer; @@ -25,7 +25,6 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; -import org.apache.commons.lang3.StringUtils; public class AutoMapperGenerator { @@ -58,10 +57,9 @@ public class AutoMapperGenerator { 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( - metadata.isCycles() ? CollectionUtil.newArrayList(source, context) : Collections.singletonList(source), + metadata.isCycles() ? CollectionUtils.newArrayList(source, context) : Collections.singletonList(source), metadata.getFieldMappingList(), targetClassName, CONVERT_METHOD_NAME)); @@ -71,14 +69,14 @@ public class AutoMapperGenerator { if (targetIsImmutable) { builder.addMethod( addEmptyConvertMethodForImmutableEntity( - metadata.isCycles() ? CollectionUtil.newArrayList(source, target, - context) : CollectionUtil.newArrayList(source, target), + metadata.isCycles() ? CollectionUtils.newArrayList(source, target, + context) : CollectionUtils.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.isCycles() ? CollectionUtils.newArrayList(source, target, + context) : CollectionUtils.newArrayList(source, target), metadata.getFieldMappingList(), targetClassName, CONVERT_METHOD_NAME)); @@ -117,7 +115,7 @@ public class AutoMapperGenerator { .addParameters(parameterSpecs) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .returns(target); - if (CollectionUtil.isNotEmpty(autoMappingMetadataList)) { + if (CollectionUtils.isNotEmpty(autoMappingMetadataList)) { methodSpecBuilder.addAnnotations(buildMappingAnnotations(autoMappingMetadataList)); } return methodSpecBuilder.build(); @@ -140,17 +138,17 @@ public class AutoMapperGenerator { builder.addMember("defaultValue", CodeBlock.builder().add("$S", autoMappingMetadata.getDefaultValue()).build()); } - if (StringUtils.isNotEmpty(autoMappingMetadata.getExpression())) { + if (StrUtil.isNotEmpty(autoMappingMetadata.getExpression())) { builder.addMember("expression", CodeBlock.builder().add("$S", autoMappingMetadata.getExpression()).build()); } else { builder.addMember("source", CodeBlock.builder().add("$S", autoMappingMetadata.getSource()).build()); } - if (StringUtils.isNotEmpty(autoMappingMetadata.getDefaultExpression())) { + if (StrUtil.isNotEmpty(autoMappingMetadata.getDefaultExpression())) { builder.addMember("defaultExpression", CodeBlock.builder().add("$S", autoMappingMetadata.getDefaultExpression()).build()); } - if (StringUtils.isNotEmpty(autoMappingMetadata.getConditionExpression())) { + if (StrUtil.isNotEmpty(autoMappingMetadata.getConditionExpression())) { builder.addMember("conditionExpression", CodeBlock.builder().add("$S", autoMappingMetadata.getConditionExpression()).build()); } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/MapperConfigGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/MapperConfigGenerator.java index 0e086f3..8dc5a9a 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/MapperConfigGenerator.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/MapperConfigGenerator.java @@ -1,18 +1,17 @@ package io.github.linpeilie.processor.generator; -import cn.hutool.core.collection.CollectionUtil; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeSpec; import io.github.linpeilie.processor.AutoMapperProperties; +import io.github.linpeilie.utils.CollectionUtils; import java.io.IOException; import java.io.Writer; import java.util.List; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Modifier; -import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import static javax.tools.Diagnostic.Kind.ERROR; @@ -44,7 +43,7 @@ public class MapperConfigGenerator { private AnnotationSpec buildMapperConfigAnnotationSpec(final String adapterClassName, final List uses) { CodeBlock.Builder usesCodeBuilder = CodeBlock.builder().add("{"); usesCodeBuilder.add("$T.class", ClassName.get(AutoMapperProperties.getAdapterPackage(), adapterClassName)); - if (CollectionUtil.isNotEmpty(uses)) { + if (CollectionUtils.isNotEmpty(uses)) { uses.forEach(use -> { usesCodeBuilder.add(", $T.class", use); }); diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/SolonAdapterMapperGenerator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/SolonAdapterMapperGenerator.java index 87c4447..bbc26bf 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/SolonAdapterMapperGenerator.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/generator/SolonAdapterMapperGenerator.java @@ -1,8 +1,8 @@ package io.github.linpeilie.processor.generator; -import cn.hutool.core.collection.CollectionUtil; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; +import io.github.linpeilie.utils.CollectionUtils; import java.util.Collection; import java.util.List; @@ -25,6 +25,6 @@ public class SolonAdapterMapperGenerator extends IocAdapterMapperGenerator { @Override protected List injectAnnotations() { - return CollectionUtil.newArrayList(inject()); + return CollectionUtils.newArrayList(inject()); } } 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 877aa96..e8f3337 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 @@ -1,18 +1,9 @@ package io.github.linpeilie.processor.generator; -import cn.hutool.core.collection.CollectionUtil; 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.ParameterSpec; -import com.squareup.javapoet.TypeSpec; -import io.github.linpeilie.processor.AbstractAdapterMapperGenerator; -import io.github.linpeilie.processor.metadata.AbstractAdapterMethodMetadata; -import java.util.Collection; +import io.github.linpeilie.utils.CollectionUtils; import java.util.List; -import javax.lang.model.element.Modifier; public class SpringAdapterMapperGenerator extends IocAdapterMapperGenerator { @@ -35,7 +26,7 @@ public class SpringAdapterMapperGenerator extends IocAdapterMapperGenerator { @Override protected List injectAnnotations() { - return CollectionUtil.newArrayList(autowired()); + return CollectionUtils.newArrayList(autowired()); } } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AbstractMapperMetadata.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AbstractMapperMetadata.java index 4225adb..1ef5515 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AbstractMapperMetadata.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AbstractMapperMetadata.java @@ -2,14 +2,14 @@ package io.github.linpeilie.processor.metadata; import com.squareup.javapoet.ClassName; import io.github.linpeilie.processor.AutoMapperProperties; -import org.apache.commons.lang3.StringUtils; +import io.github.linpeilie.utils.StrUtil; public abstract class AbstractMapperMetadata { protected ClassName sourceClassName; public String mapperPackage() { - return StringUtils.isNotEmpty(AutoMapperProperties.getMapperPackage()) + return StrUtil.isNotEmpty(AutoMapperProperties.getMapperPackage()) ? AutoMapperProperties.getMapperPackage() : sourceClassName.packageName(); } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapMapperMetadata.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapMapperMetadata.java index b22e27c..5438689 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapMapperMetadata.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapMapperMetadata.java @@ -1,13 +1,13 @@ package io.github.linpeilie.processor.metadata; import io.github.linpeilie.processor.AutoMapperProperties; -import org.apache.commons.lang3.StringUtils; +import io.github.linpeilie.utils.StrUtil; public class AutoMapMapperMetadata extends AutoMapperMetadata { @Override public String mapperPackage() { - return StringUtils.isNotEmpty(AutoMapperProperties.getMapperPackage()) - ? AutoMapperProperties.getMapperPackage() : getTargetClassName().packageName(); + return StrUtil.isNotEmpty( + AutoMapperProperties.getMapperPackage()) ? AutoMapperProperties.getMapperPackage() : getTargetClassName().packageName(); } } diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/solon/SolonComponentProcessor.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/solon/SolonComponentProcessor.java index 168668d..4c6aa63 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/solon/SolonComponentProcessor.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/solon/SolonComponentProcessor.java @@ -1,8 +1,7 @@ package io.github.linpeilie.processor.solon; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.collection.ListUtil; import io.github.linpeilie.ComponentModelConstant; +import io.github.linpeilie.utils.CollectionUtils; import java.util.List; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; @@ -16,7 +15,7 @@ public class SolonComponentProcessor extends AnnotationBasedComponentModelProces @Override protected List getTypeAnnotations(final Mapper mapper) { - return CollectionUtil.newArrayList(component()); + return CollectionUtils.newArrayList(component()); } private Annotation component() { @@ -29,7 +28,7 @@ public class SolonComponentProcessor extends AnnotationBasedComponentModelProces @Override protected List getMapperReferenceAnnotations() { - return CollectionUtil.newArrayList(inject()); + return CollectionUtils.newArrayList(inject()); } @Override diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ExceptionUtils.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ExceptionUtils.java new file mode 100644 index 0000000..3573c67 --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ExceptionUtils.java @@ -0,0 +1,15 @@ +package io.github.linpeilie.processor.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class ExceptionUtils { + + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + +} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileReader.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileReader.java new file mode 100644 index 0000000..c4d71b2 --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileReader.java @@ -0,0 +1,61 @@ +package io.github.linpeilie.processor.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.util.Collection; + +public class FileReader extends FileWrapper { + private static final long serialVersionUID = 1L; + + public static FileReader create(File file, Charset charset){ + return new FileReader(file, charset); + } + + // ------------------------------------------------------- Constructor start + public FileReader(File file, Charset charset) { + super(file, charset); + checkFile(); + } + + // ------------------------------------------------------- Constructor end + + public > T readLines(T collection) { + BufferedReader reader = null; + try { + reader = FileUtils.getReader(file, charset); + String line; + while (true) { + line = reader.readLine(); + if (line == null) { + break; + } + collection.add(line); + } + return collection; + } catch (IOException e) { + throw new UncheckedIOException(e); + } finally { + if (null != reader) { + try { + reader.close(); + } catch (Exception e) { + // 静默关闭 + } + } + } + } + + private void checkFile() { + if (file == null) { + throw new UncheckedIOException(new IOException("File to write content is null !")); + } + if (this.file.exists() && !file.isFile()) { + throw new UncheckedIOException( + new IOException("File [" + this.file.getAbsoluteFile() + "] is not a file !")); + } + } + +} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileUtils.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileUtils.java new file mode 100644 index 0000000..6ed0266 --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileUtils.java @@ -0,0 +1,145 @@ +package io.github.linpeilie.processor.utils; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class FileUtils { + + public static File writeUtf8Lines(Collection list, File file) { + return writeLines(list, file, StandardCharsets.UTF_8); + } + + public static File writeLines(Collection list, File file, Charset charset) { + return writeLines(list, file, charset, false); + } + + public static File writeLines(Collection list, File file, Charset charset, boolean isAppend) { + return FileWriter.create(file, charset).writeLines(list, isAppend); + } + + public static List readUtf8Lines(File file) { + return readLines(file, StandardCharsets.UTF_8); + } + + public static List readLines(File file, Charset charset) { + return readLines(file, charset, new ArrayList<>()); + } + + public static > T readLines(File file, Charset charset, T collection) { + return FileReader.create(file, charset).readLines(collection); + } + + public static BufferedInputStream getInputStream(File file) throws IOException { + return new BufferedInputStream(Files.newInputStream(file.toPath())); + } + + public static BufferedReader getReader(File file, Charset charset) throws IOException { + BufferedInputStream in = getInputStream(file); + InputStreamReader reader; + if (null == charset) { + reader = new InputStreamReader(in); + } else { + reader = new InputStreamReader(in, charset); + } + + return new BufferedReader(reader); + } + + public static File mkParentDirs(File file) { + if (null == file) { + return null; + } + return mkdir(getParent(file, 1)); + } + + public static File getParent(File file, int level) { + if (level < 1 || null == file) { + return file; + } + + File parentFile; + try { + parentFile = file.getCanonicalFile().getParentFile(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + if (1 == level) { + return parentFile; + } + return getParent(parentFile, level - 1); + } + + public static File mkdir(File dir) { + if (dir == null) { + return null; + } + if (false == dir.exists()) { + mkdirsSafely(dir, 5, 1); + } + return dir; + } + + public static boolean mkdirsSafely(File dir, int tryCount, long sleepMillis) { + if (dir == null) { + return false; + } + if (dir.isDirectory()) { + return true; + } + for (int i = 1; i <= tryCount; i++) { // 高并发场景下,可以看到 i 处于 1 ~ 3 之间 + // 如果文件已存在,也会返回 false,所以该值不能作为是否能创建的依据,因此不对其进行处理 + //noinspection ResultOfMethodCallIgnored + dir.mkdirs(); + if (dir.exists()) { + return true; + } + ThreadUtils.sleep(sleepMillis); + } + return dir.exists(); + } + + public static File touch(File file) { + if (null == file) { + return null; + } + if (false == file.exists()) { + mkParentDirs(file); + try { + //noinspection ResultOfMethodCallIgnored + file.createNewFile(); + } catch (Exception e) { + throw new UncheckedIOException(new IOException(e)); + } + } + return file; + } + + public static boolean isNotEmpty(File file) { + return !isEmpty(file); + } + + public static boolean isEmpty(File file) { + if (null == file || !file.exists()) { + return true; + } + + if (file.isDirectory()) { + String[] subFiles = file.list(); + return subFiles == null || subFiles.length == 0; + } else if (file.isFile()) { + return file.length() <= 0; + } + + return false; + } +} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileWrapper.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileWrapper.java new file mode 100644 index 0000000..d26f740 --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileWrapper.java @@ -0,0 +1,68 @@ +package io.github.linpeilie.processor.utils; + +import java.io.File; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 文件包装器,扩展文件对象 + * + * @author Looly + * + */ +public class FileWrapper implements Serializable { + private static final long serialVersionUID = 1L; + + protected File file; + protected Charset charset; + + /** 默认编码:UTF-8 */ + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + // ------------------------------------------------------- Constructor start + public FileWrapper(File file, Charset charset) { + this.file = file; + this.charset = charset; + } + // ------------------------------------------------------- Constructor end + + // ------------------------------------------------------- Setters and Getters start start + /** + * 获得文件 + * @return 文件 + */ + public File getFile() { + return file; + } + + /** + * 设置文件 + * @param file 文件 + * @return 自身 + */ + public FileWrapper setFile(File file) { + this.file = file; + return this; + } + + /** + * 获得字符集编码 + * @return 编码 + */ + public Charset getCharset() { + return charset; + } + + /** + * 设置字符集编码 + * @param charset 编码 + * @return 自身 + */ + public FileWrapper setCharset(Charset charset) { + this.charset = charset; + return this; + } + // ------------------------------------------------------- Setters and Getters start end + +} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileWriter.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileWriter.java new file mode 100644 index 0000000..4d3691e --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/FileWriter.java @@ -0,0 +1,89 @@ +package io.github.linpeilie.processor.utils; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; + +public class FileWriter extends FileWrapper { + + private static final long serialVersionUID = 1L; + + public static FileWriter create(File file, Charset charset) { + return new FileWriter(file, charset); + } + + // ------------------------------------------------------- Constructor start + + public FileWriter(File file, Charset charset) { + super(file, charset); + checkFile(); + } + + // ------------------------------------------------------- Constructor end + + public File writeLines(Iterable list, boolean isAppend) { + return writeLines(list, null, isAppend); + } + + public BufferedWriter getWriter(boolean isAppend) { + try { + return new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(FileUtils.touch(file), isAppend), charset)); + } catch (Exception e) { + throw new UncheckedIOException(new IOException(e)); + } + } + + public PrintWriter getPrintWriter(boolean isAppend) { + return new PrintWriter(getWriter(isAppend)); + } + + public File writeLines(Iterable list, LineSeparator lineSeparator, boolean isAppend) { + try (PrintWriter writer = getPrintWriter(isAppend)) { + boolean isFirst = true; + for (T t : list) { + if (null != t) { + if (isFirst) { + isFirst = false; + if (isAppend && FileUtils.isNotEmpty(this.file)) { + // 追加模式下且文件非空,补充换行符 + printNewLine(writer, lineSeparator); + } + } else { + printNewLine(writer, lineSeparator); + } + writer.print(t); + + writer.flush(); + } + } + } + return this.file; + } + + private void printNewLine(PrintWriter writer, LineSeparator lineSeparator) { + if (null == lineSeparator) { + //默认换行符 + writer.println(); + } else { + //自定义换行符 + writer.print(lineSeparator.getValue()); + } + } + + private void checkFile() { + if (file == null) { + throw new UncheckedIOException(new IOException("File to write content is null !")); + } + if (this.file.exists() && !file.isFile()) { + throw new UncheckedIOException( + new IOException("File [" + this.file.getAbsoluteFile() + "] is not a file !")); + } + } + +} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/LineSeparator.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/LineSeparator.java new file mode 100644 index 0000000..e7cb198 --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/LineSeparator.java @@ -0,0 +1,20 @@ +package io.github.linpeilie.processor.utils; + +public enum LineSeparator { + /** Mac系统换行符:"\r" */ + MAC("\r"), + /** Linux系统换行符:"\n" */ + LINUX("\n"), + /** Windows系统换行符:"\r\n" */ + WINDOWS("\r\n"); + + private final String value; + + LineSeparator(String lineSeparator) { + this.value = lineSeparator; + } + + public String getValue() { + return this.value; + } +} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ObjectUtils.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ObjectUtils.java new file mode 100644 index 0000000..db76207 --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ObjectUtils.java @@ -0,0 +1,9 @@ +package io.github.linpeilie.processor.utils; + +public class ObjectUtils { + + public static T defaultIfNull(final T object, final T defaultValue) { + return object != null ? object : defaultValue; + } + +} diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ThreadUtils.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ThreadUtils.java new file mode 100644 index 0000000..0dcefc9 --- /dev/null +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/utils/ThreadUtils.java @@ -0,0 +1,14 @@ +package io.github.linpeilie.processor.utils; + +public class ThreadUtils { + public static boolean sleep(long millis) { + if (millis > 0) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + return false; + } + } + return true; + } +} diff --git a/mapstruct-plus/pom.xml b/mapstruct-plus/pom.xml index cca4476..faa09f6 100644 --- a/mapstruct-plus/pom.xml +++ b/mapstruct-plus/pom.xml @@ -24,8 +24,8 @@ mapstruct - cn.hutool - hutool-core + io.github.linpeilie + mapstruct-plus-object-convert 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 d26ab43..75ba1f6 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseCycleAvoidingMapper.java @@ -1,6 +1,5 @@ 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; 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 a784b66..a89fa3e 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/BaseMapper.java @@ -1,9 +1,8 @@ package io.github.linpeilie; -import cn.hutool.core.collection.CollectionUtil; import io.github.linpeilie.annotations.DoIgnore; +import io.github.linpeilie.utils.CollectionUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.mapstruct.MappingTarget; @@ -18,7 +17,7 @@ public interface BaseMapper { @DoIgnore default List convert(List sourceList) { - if (CollectionUtil.isEmpty(sourceList)) { + if (CollectionUtils.isEmpty(sourceList)) { return new ArrayList<>(); } return sourceList.stream().map(this::convert).collect(Collectors.toList()); diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/utils/CharUtils.java b/mapstruct-plus/src/main/java/io/github/linpeilie/utils/CharUtils.java new file mode 100644 index 0000000..284600d --- /dev/null +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/utils/CharUtils.java @@ -0,0 +1,41 @@ +package io.github.linpeilie.utils; + +public class CharUtils { + /** + * 是否空白符
+ * 空白符包括空格、制表符、全角空格和不间断空格
+ * + * @param c 字符 + * @return 是否空白符 + * @see Character#isWhitespace(int) + * @see Character#isSpaceChar(int) + * @since 4.0.10 + */ + public static boolean isBlankChar(char c) { + return isBlankChar((int) c); + } + + /** + * 是否空白符
+ * 空白符包括空格、制表符、全角空格和不间断空格
+ * + * @param c 字符 + * @return 是否空白符 + * @see Character#isWhitespace(int) + * @see Character#isSpaceChar(int) + * @since 4.0.10 + */ + public static boolean isBlankChar(int c) { + return Character.isWhitespace(c) + || Character.isSpaceChar(c) + || c == '\ufeff' + || c == '\u202a' + || c == '\u0000' + // issue#I5UGSQ,Hangul Filler + || c == '\u3164' + // Braille Pattern Blank + || c == '\u2800' + // MONGOLIAN VOWEL SEPARATOR + || c == '\u180e'; + } +} diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/utils/CollectionUtils.java b/mapstruct-plus/src/main/java/io/github/linpeilie/utils/CollectionUtils.java new file mode 100644 index 0000000..857c0f1 --- /dev/null +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/utils/CollectionUtils.java @@ -0,0 +1,29 @@ +package io.github.linpeilie.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class CollectionUtils { + + public static boolean isEmpty(Collection collection) { + return collection == null || collection.isEmpty(); + } + + public static boolean isNotEmpty(Collection collection) { + return !isEmpty(collection); + } + + public static List newArrayList(T... values) { + if (values == null || values.length == 0) { + return new ArrayList<>(); + } + List arrayList = new ArrayList<>(values.length); + Collections.addAll(arrayList, values); + return arrayList; + } + +} diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/utils/StrUtil.java b/mapstruct-plus/src/main/java/io/github/linpeilie/utils/StrUtil.java new file mode 100644 index 0000000..765e62d --- /dev/null +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/utils/StrUtil.java @@ -0,0 +1,86 @@ +package io.github.linpeilie.utils; + +public class StrUtil { + + /** + * 去掉首部指定长度的字符串并将剩余字符串首字母小写
例如:str=setName, preLength=3 =》 return name + * + * @param str 被处理的字符串 + * @param preLength 去掉的长度 + * @return 处理后的字符串,不符合规范返回null + */ + public static String removePreAndLowerFirst(CharSequence str, int preLength) { + if (str == null) { + return null; + } + if (str.length() > preLength) { + char first = Character.toLowerCase(str.charAt(preLength)); + if (str.length() > preLength + 1) { + return first + str.toString().substring(preLength + 1); + } + return String.valueOf(first); + } else { + return str.toString(); + } + } + + /** + * 获得set或get或is方法对应的标准属性名
例如:setName 返回 name + * + *
+     * getName =》name
+     * setName =》name
+     * isName  =》name
+     * 
+ * + * @param getOrSetMethodName Get或Set方法名 + * @return 如果是set或get方法名,返回field, 否则null + */ + public static String getGeneralField(CharSequence getOrSetMethodName) { + final String getOrSetMethodNameStr = getOrSetMethodName.toString(); + if (getOrSetMethodNameStr.startsWith("get") || getOrSetMethodNameStr.startsWith("set")) { + return removePreAndLowerFirst(getOrSetMethodName, 3); + } else if (getOrSetMethodNameStr.startsWith("is")) { + return removePreAndLowerFirst(getOrSetMethodName, 2); + } + return null; + } + + public static boolean isEmpty(CharSequence cs) { + return cs == null || cs.length() == 0; + } + + public static boolean isNotEmpty(CharSequence cs) { + return !isEmpty(cs); + } + + public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) { + if (null == str1) { + // 只有两个都为null才判断相等 + return str2 == null; + } + if (null == str2) { + // 字符串2空,字符串1非空,直接false + return false; + } + + return str1.toString().equalsIgnoreCase(str2.toString()); + } + + public static boolean isBlank(CharSequence str) { + final int length; + if ((str == null) || ((length = str.length()) == 0)) { + return true; + } + + for (int i = 0; i < length; i++) { + // 只要有一个非空字符即为非空字符串 + if (false == CharUtils.isBlankChar(str.charAt(i))) { + return false; + } + } + + return true; + } + +} diff --git a/pom.xml b/pom.xml index ede6ffd..c6c0fb1 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ mapstruct-plus mapstruct-plus-spring-boot-starter mapstruct-plus-processor + mapstruct-plus-object-convert @@ -22,12 +23,17 @@ 8 UTF-8 1.5.5.Final - 5.8.15 + 5.8.26 https://github.com/linpeilie/mapstruct-plus.git + + io.github.linpeilie + mapstruct-plus-object-convert + ${mapstruct-plus.version} + io.github.linpeilie mapstruct-plus @@ -43,11 +49,6 @@ javapoet 1.9.0 - - org.apache.commons - commons-lang3 - 3.12.0 - org.mapstruct mapstruct -- Gitee From 85ffe0e27f74e9ac225405a0f91b9179c765bafa Mon Sep 17 00:00:00 2001 From: linpeilie Date: Wed, 13 Mar 2024 10:10:27 +0800 Subject: [PATCH 07/12] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E4=B8=AD=E4=B8=8D=E5=AD=98=E5=9C=A8=E8=BD=AC=E6=8D=A2=E7=B1=BB?= =?UTF-8?q?=E7=9A=84=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github/linpeilie/processor/AutoMapperProcessor.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 78986c8..9d95cf1 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 @@ -322,6 +322,10 @@ public class AutoMapperProcessor extends AbstractProcessor { addAdapterMapMethod(metadata); }); + if (mapMethodMap.isEmpty()) { + return; + } + adapterMapperGenerator.write(processingEnv, mapMethodMap.values(), AutoMapperProperties.getMapAdapterClassName(), @@ -503,6 +507,10 @@ public class AutoMapperProcessor extends AbstractProcessor { addAdapterMethod(metadata); }); + if (methodMap.isEmpty()) { + return; + } + adapterMapperGenerator.write(processingEnv, methodMap.values(), AutoMapperProperties.getAdapterClassName(), -- Gitee From 7e2118a4fa23b8a69db5fb168d9efc9a1dd54200 Mon Sep 17 00:00:00 2001 From: linpeilie Date: Wed, 13 Mar 2024 13:41:40 +0800 Subject: [PATCH 08/12] =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B3=9B=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../linpeilie/processor/AbstractAdapterMapperGenerator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 9ca5cc4..0e62e66 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 @@ -125,7 +125,8 @@ public abstract class AbstractAdapterMapperGenerator { PARAM__PARAMETER_NAME ).build(); MethodSpec methodSpecForList = - buildDefaultProxyMethod(adapterMethodMetadata, listParameter, ClassName.get("java.util", "List"), + buildDefaultProxyMethod(adapterMethodMetadata, listParameter, + ParameterizedTypeName.get(ClassName.get("java.util", "List"), adapterMethodMetadata.getReturn()), annotation); methodSpecs.add(methodSpecForList); } -- Gitee From d34c4176810e501ee410bd70e041c20d7456cd70 Mon Sep 17 00:00:00 2001 From: linpeilie Date: Wed, 13 Mar 2024 14:52:00 +0800 Subject: [PATCH 09/12] =?UTF-8?q?CycleAvoidingMappingContext=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=B1=BB=E5=9E=8B=E4=BD=9C=E4=B8=BA=E4=B8=8B=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CycleAvoidingMappingContext.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/CycleAvoidingMappingContext.java b/mapstruct-plus/src/main/java/io/github/linpeilie/CycleAvoidingMappingContext.java index edd82af..0e10832 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/CycleAvoidingMappingContext.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/CycleAvoidingMappingContext.java @@ -1,5 +1,6 @@ package io.github.linpeilie; +import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import org.mapstruct.BeforeMapping; @@ -8,16 +9,28 @@ import org.mapstruct.TargetType; public class CycleAvoidingMappingContext { - private Map knownInstances = new IdentityHashMap(); + private Map, Object>> knownInstances = new IdentityHashMap<>(); @BeforeMapping public T getMappedInstance(Object source, @TargetType Class targetType) { - return (T) knownInstances.get( source ); + Map, Object> map = knownInstances.get(source); + if (map == null || map.isEmpty()) { + return null; + } + return (T) map.get(targetType); } @BeforeMapping public void storeMappedInstance(Object source, @MappingTarget Object target) { - knownInstances.put( source, target ); + if (target == null) { + return; + } + Map, Object> map = knownInstances.get(source); + if (map == null) { + map = new HashMap<>(); + } + map.put(target.getClass(), target); + knownInstances.put( source, map ); } } -- Gitee From 7cf7132d1d06a209f87eab0b748c786ec09ffbb7 Mon Sep 17 00:00:00 2001 From: linpeilie Date: Thu, 14 Mar 2024 10:42:47 +0800 Subject: [PATCH 10/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=BE=AA=E7=8E=AF=E5=B5=8C=E5=A5=97=E7=9A=84?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/github/linpeilie/model/Employee.java | 2 +- .../io/github/linpeilie/model/Product.java | 2 +- .../io/github/linpeilie/model/ProductDto.java | 2 +- .../linpeilie/model/ProductProperty.java | 2 +- .../linpeilie/model/ProductPropertyDto.java | 2 +- .../AbstractAdapterMapperGenerator.java | 16 ++++++++-------- .../processor/AutoMapperProcessor.java | 18 +++++++++--------- .../generator/AutoMapperGenerator.java | 6 +++--- .../processor/metadata/AutoMapperMetadata.java | 12 +++++------- .../linpeilie/annotations/AutoMapper.java | 9 ++++++--- 10 files changed, 36 insertions(+), 35 deletions(-) diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee.java index 92d4546..804bf08 100644 --- a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee.java +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Employee.java @@ -5,7 +5,7 @@ import java.util.List; import lombok.Data; @Data -@AutoMapper(target = EmployeeDto.class, cycles = true) +@AutoMapper(target = EmployeeDto.class, cycleAvoiding = true) public class Employee { private String name; diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java index 590005a..fcd4eec 100644 --- a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java @@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; @Data -@AutoMapper(target = ProductDto.class, cycles = true) +@AutoMapper(target = ProductDto.class, cycleAvoiding = true) public class Product { private Long id; diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductDto.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductDto.java index 53c4051..d3d238f 100644 --- a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductDto.java +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/ProductDto.java @@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; @Data -@AutoMapper(target = Product.class, cycles = true) +@AutoMapper(target = Product.class, cycleAvoiding = true) public class ProductDto { private Long id; 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 d254959..1c95349 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 @@ -6,7 +6,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; @Data -@AutoMapper(target = ProductPropertyDto.class, cycles = true) +@AutoMapper(target = ProductPropertyDto.class, cycleAvoiding = 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 79a0ed7..1797d31 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 @@ -6,7 +6,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; @Data -@AutoMapper(target = ProductProperty.class, cycles = true) +@AutoMapper(target = ProductProperty.class, cycleAvoiding = 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 0e62e66..5715448 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 @@ -32,8 +32,8 @@ public abstract class AbstractAdapterMapperGenerator { public void write(ProcessingEnvironment processingEnv, Collection adapterMethods, String adapterClassName, - boolean cycle) { - write(processingEnv, createAdapterTypeSpec(adapterClassName, adapterMethods, cycle)); + boolean cycleAvoiding) { + write(processingEnv, createAdapterTypeSpec(adapterClassName, adapterMethods, cycleAvoiding)); } private void write(ProcessingEnvironment processingEnv, TypeSpec typeSpec) { @@ -55,17 +55,17 @@ public abstract class AbstractAdapterMapperGenerator { protected TypeSpec createAdapterTypeSpec(String adapterClassName, Collection adapterMethods, - boolean cycle) { + boolean cycleAvoiding) { List methods = adapterMethods.stream() - .filter(method -> !cycle || method.needCycleAvoiding()) - .map(method -> buildProxyMethod(method, cycle)) + .filter(method -> !cycleAvoiding || method.needCycleAvoiding()) + .map(method -> buildProxyMethod(method, cycleAvoiding)) .flatMap(Collection::stream) .collect(Collectors.toList()); if (methods.isEmpty()) { return null; } return createTypeSpec(methods, adapterClassName, - cycle ? ClassName.get(adapterPackage(), AutoMapperProperties.getAdapterClassName()) : null); + cycleAvoiding ? ClassName.get(adapterPackage(), AutoMapperProperties.getAdapterClassName()) : null); } protected TypeSpec createTypeSpec(List methods, String adapterClassName, ClassName superClass) { @@ -97,9 +97,9 @@ public abstract class AbstractAdapterMapperGenerator { return source; } - private List buildProxyMethod(AbstractAdapterMethodMetadata adapterMethod, boolean cycle) { + private List buildProxyMethod(AbstractAdapterMethodMetadata adapterMethod, boolean cycleAvoiding) { List methodSpecs = new ArrayList<>(); - if (cycle) { + if (cycleAvoiding) { methodSpecs.addAll(buildCycleAvoidingProxyMethod(adapterMethod)); } else { methodSpecs.addAll(buildDefaultProxyMethod(adapterMethod, null)); 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 9d95cf1..b98d605 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 @@ -540,12 +540,12 @@ public class AutoMapperProcessor extends AbstractProcessor { AutoMapperMetadata reverseMapperMetadata = initAutoMapperMetadata(autoMapperMetadata.getTargetClassName(), autoMapperMetadata.getSourceClassName(), - autoMapperMetadata.isCycles()); + autoMapperMetadata.isCycleAvoiding()); reverseMapperMetadata.setConvertGenerate(autoMapperMetadata.isReverseConvertGenerate()); reverseMapperMetadata.setUsesClassNameList(autoMapperMetadata.getUsesClassNameList()); reverseMapperMetadata.setImportsClassNameList(autoMapperMetadata.getImportsClassNameList()); - reverseMapperMetadata.setCycles(autoMapperMetadata.isCycles()); - if (reverseMapperMetadata.isCycles()) { + reverseMapperMetadata.setCycleAvoiding(autoMapperMetadata.isCycleAvoiding()); + if (reverseMapperMetadata.isCycleAvoiding()) { reverseMapperMetadata.setSuperClass(ClassName.get(ContextConstants.BaseCycleAvoidingMapper.packageName, ContextConstants.BaseCycleAvoidingMapper.className)); } else { @@ -588,7 +588,7 @@ public class AutoMapperProcessor extends AbstractProcessor { } AdapterMethodMetadata adapterMethodMetadata = AdapterMethodMetadata.newInstance(metadata.getSourceClassName(), metadata.getTargetClassName(), - metadata.mapperClass(), metadata.isCycles()); + metadata.mapperClass(), metadata.isCycleAvoiding()); methodMap.putIfAbsent(adapterMethodMetadata.getMethodName(), adapterMethodMetadata); } @@ -598,14 +598,14 @@ public class AutoMapperProcessor extends AbstractProcessor { mapMethodMap.putIfAbsent(adapterMapMethodMetadata.getMethodName(), adapterMapMethodMetadata); } - private AutoMapperMetadata initAutoMapperMetadata(ClassName source, ClassName target, boolean cycles) { + private AutoMapperMetadata initAutoMapperMetadata(ClassName source, ClassName target, boolean cycleAvoiding) { AutoMapperMetadata metadata = new AutoMapperMetadata(); metadata.setSourceClassName(source); metadata.setTargetClassName(target); metadata.setSuperGenerics(new ClassName[] {source, target}); ClassName mapStructConfigClass; - if (cycles) { + if (cycleAvoiding) { mapStructConfigClass = ClassName.get(AutoMapperProperties.getConfigPackage(), AutoMapperProperties.getCycleAvoidingConfigClassName()); } else { @@ -680,7 +680,7 @@ public class AutoMapperProcessor extends AbstractProcessor { List reverseMappingMetadataList = buildFieldReverseMappingMetadata((TypeElement) ele); reverseMappingMetadataList.removeIf(mappingMetadata -> !isTargetFieldMapping(target, mappingMetadata)); - AutoMapperMetadata metadata = initAutoMapperMetadata(source, target, autoMapper.cycles()); + AutoMapperMetadata metadata = initAutoMapperMetadata(source, target, autoMapper.cycleAvoiding()); metadata.setUsesClassNameList(uses); metadata.setImportsClassNameList(importsClassNameList); @@ -688,8 +688,8 @@ public class AutoMapperProcessor extends AbstractProcessor { metadata.setFieldReverseMappingList(reverseMappingMetadataList); metadata.setConvertGenerate(autoMapper.convertGenerate()); metadata.setReverseConvertGenerate(autoMapper.reverseConvertGenerate()); - metadata.setCycles(autoMapper.cycles()); - if (metadata.isCycles()) { + metadata.setCycleAvoiding(autoMapper.cycleAvoiding()); + if (metadata.isCycleAvoiding()) { metadata.setSuperClass(ClassName.get(ContextConstants.BaseCycleAvoidingMapper.packageName, ContextConstants.BaseCycleAvoidingMapper.className)); } else { 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 9dda68c..31809c5 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 @@ -59,7 +59,7 @@ public class AutoMapperGenerator { .build(); if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) { builder.addMethod(addConvertMethodSpec( - metadata.isCycles() ? CollectionUtils.newArrayList(source, context) : Collections.singletonList(source), + metadata.isCycleAvoiding() ? CollectionUtils.newArrayList(source, context) : Collections.singletonList(source), metadata.getFieldMappingList(), targetClassName, CONVERT_METHOD_NAME)); @@ -69,13 +69,13 @@ public class AutoMapperGenerator { if (targetIsImmutable) { builder.addMethod( addEmptyConvertMethodForImmutableEntity( - metadata.isCycles() ? CollectionUtils.newArrayList(source, target, + metadata.isCycleAvoiding() ? CollectionUtils.newArrayList(source, target, context) : CollectionUtils.newArrayList(source, target), targetClassName, CONVERT_METHOD_NAME)); } else if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) { builder.addMethod(addConvertMethodSpec( - metadata.isCycles() ? CollectionUtils.newArrayList(source, target, + metadata.isCycleAvoiding() ? CollectionUtils.newArrayList(source, target, context) : CollectionUtils.newArrayList(source, target), metadata.getFieldMappingList(), targetClassName, diff --git a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java index dc1a598..30ace3e 100644 --- a/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java +++ b/mapstruct-plus-processor/src/main/java/io/github/linpeilie/processor/metadata/AutoMapperMetadata.java @@ -1,9 +1,7 @@ package io.github.linpeilie.processor.metadata; import com.squareup.javapoet.ClassName; -import io.github.linpeilie.processor.AutoMapperProperties; import java.util.List; -import org.mapstruct.ReportingPolicy; public class AutoMapperMetadata extends AbstractMapperMetadata { @@ -27,7 +25,7 @@ public class AutoMapperMetadata extends AbstractMapperMetadata { private boolean reverseConvertGenerate; - private boolean cycles; + private boolean cycleAvoiding; public String mapperName() { return sourceClassName.simpleName() + "To" + targetClassName.simpleName() + "Mapper"; @@ -116,11 +114,11 @@ public class AutoMapperMetadata extends AbstractMapperMetadata { this.convertGenerate = convertGenerate; } - public boolean isCycles() { - return cycles; + public boolean isCycleAvoiding() { + return cycleAvoiding; } - public void setCycles(boolean cycles) { - this.cycles = cycles; + public void setCycleAvoiding(boolean cycleAvoiding) { + this.cycleAvoiding = cycleAvoiding; } } diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapper.java b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapper.java index 8beae7b..fc2b90c 100644 --- a/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapper.java +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/annotations/AutoMapper.java @@ -33,10 +33,13 @@ public @interface AutoMapper { boolean reverseConvertGenerate() default true; /** - * 是否有循环依赖对象 + * 是否需要避免对象循环嵌套 + *

+ * 循环嵌套:A中有属性类型B,B中有属性类型A,且可能对象之间互相引用 + *

* - * @return true: 有循环依赖对象 false: 没有循环依赖对象 + * @return true: 需要避免对象循环嵌套;false:不需要 */ - boolean cycles() default false; + boolean cycleAvoiding() default false; } -- Gitee From 182bc024daab34b619336594242017e38b55c56f Mon Sep 17 00:00:00 2001 From: linpeilie Date: Thu, 14 Mar 2024 20:25:03 +0800 Subject: [PATCH 11/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 131 +++++++++++++++++- example/pom.xml | 2 +- .../linpeilie/mapper/TreeNodeAwareMapper.java | 19 +++ .../io/github/linpeilie/model/Product.java | 1 + .../io/github/linpeilie/model/TreeNode.java | 18 +++ .../github/linpeilie/model/TreeNodeDto.java | 15 ++ .../AbstractAdapterMapperGenerator.java | 2 +- .../linpeilie/processor/ContextConstants.java | 5 + .../generator/AutoMapperGenerator.java | 2 + .../io/github/linpeilie/utils/ArrayUtil.java | 23 +++ pom.xml | 2 +- 11 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/mapper/TreeNodeAwareMapper.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/TreeNode.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/TreeNodeDto.java create mode 100644 mapstruct-plus/src/main/java/io/github/linpeilie/utils/ArrayUtil.java diff --git a/README.md b/README.md index 41fa218..34f15fb 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,135 @@ # mapstruct-plus +## What is MapStruct Plus + +MapStruct Plus is an enhancement to the MapStruct framework. It only does the enhancement, does not make the modification, and can automatically generate the transformation operation between two classes through an annotation, omitting the operation of defining the interface of MapStruct, makes Java type conversion easy and elegant. + +Goal: To be the simplest and most powerful type conversion tool + +**If this project helps you, hope to click a Star to encourage it!** + +## Link + +- [Document](https://mapstruct.plus) + +## Other open source projects + +- **EasyRelation**:[GitHub](https://github.com/linpeilie/easy-relation) | [Gitee](https://gitee.com/easii/easy-relation) | [Document](https://easy-relation.easii.cn) + +## Quick start + + +The following shows how to convert two objects using MapStructPlus. + +Suppose there are two classes, `UserDto` and `User`, representing the data-layer object and business-layer object, respectively: + +- `UserDto` + +```java +public class UserDto { + private String username; + private int age; + private boolean young; + + // getter、setter、toString、equals、hashCode +} +``` + +- `User` + +```java +public class User { + private String username; + private int age; + private boolean young; + + // getter、setter、toString、equals、hashCode +} +``` + +Introducing `mapstruct-plus-spring-boot-starter` dependencies: + +```xml + + latest version + + + + io.github.linpeilie + mapstruct-plus-spring-boot-starter + ${mapstruct-plus.version} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + io.github.linpeilie + mapstruct-plus-processor + ${mapstruct-plus.version} + + + + + + +``` + +Test + +```java +@SpringBootTest +public class QuickStartTest { + + @Autowired + private Converter converter; + + @Test + public void test() { + User user = new User(); + user.setUsername("jack"); + user.setAge(23); + user.setYoung(false); + + UserDto userDto = converter.convert(user, UserDto.class); + System.out.println(userDto); // UserDto{username='jack', age=23, young=false} + + assert user.getUsername().equals(userDto.getUsername()); + assert user.getAge() == userDto.getAge(); + assert user.isYoung() == userDto.isYoung(); + + User newUser = converter.convert(userDto, User.class); + + System.out.println(newUser); // User{username='jack', age=23, young=false} + + assert user.getUsername().equals(newUser.getUsername()); + assert user.getAge() == newUser.getAge(); + assert user.isYoung() == newUser.isYoung(); + } + +} +``` + +## Summary + +With the introduction of dependencies, the steps to using MapStructPlus are very simple. + +1. Add an `AutoMapper` annotation to the class you want to convert +2. Get the `Converter` instance and call the convert method. + +-------- + + ## 这是什么? -Mapstruct Plus 是对 Mapstruct 框架的一个增强,只做增强,不做修改,可以通过一个注解,自动生成两个类之间的转换操作,省略了 Mapstruct 定义接口的操作,使 Java 类型转换更加便捷、优雅。 +MapStruct Plus 是对 MapStruct 框架的一个增强,只做增强,不做修改,可以通过一个注解,自动生成两个类之间的转换操作,省略了 Mapstruct 定义接口的操作,使 Java 类型转换更加便捷、优雅。 目标:做最简单、最强大的类型转换工具 @@ -61,7 +188,7 @@ public class User { ```xml - 1.3.4 + 1.4.0 diff --git a/example/pom.xml b/example/pom.xml index 4c9dbbd..40528de 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -18,7 +18,7 @@ UTF-8 1.5.1.Final - 1.3.7-SNAPSHOTS + 1.4.0 1.18.22 5.8.26 diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/mapper/TreeNodeAwareMapper.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/mapper/TreeNodeAwareMapper.java new file mode 100644 index 0000000..5beab40 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/mapper/TreeNodeAwareMapper.java @@ -0,0 +1,19 @@ +package io.github.linpeilie.mapper; + +import io.github.linpeilie.model.TreeNode; +import java.util.List; +import org.mapstruct.Condition; +import org.mapstruct.Named; +import org.springframework.boot.context.properties.bind.Name; +import org.springframework.stereotype.Component; + +@Component +public class TreeNodeAwareMapper { + + @Condition + @Named("limitLeastTwo") + public boolean limitLeastTwo(List treeNodeList) { + return treeNodeList != null && treeNodeList.size() >= 2; + } + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java index fcd4eec..2c92172 100644 --- a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/Product.java @@ -1,6 +1,7 @@ package io.github.linpeilie.model; import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.AutoMapping; import java.util.List; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/TreeNode.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/TreeNode.java new file mode 100644 index 0000000..71a2140 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/TreeNode.java @@ -0,0 +1,18 @@ +package io.github.linpeilie.model; + +import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.AutoMapping; +import io.github.linpeilie.mapper.TreeNodeAwareMapper; +import java.util.List; +import lombok.Data; + +@Data +@AutoMapper(target = TreeNodeDto.class, cycleAvoiding = true, uses = TreeNodeAwareMapper.class) +public class TreeNode { + + private TreeNode parent; + + @AutoMapping(conditionQualifiedByName = "limitLeastTwo") + private List children; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/TreeNodeDto.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/TreeNodeDto.java new file mode 100644 index 0000000..72fe752 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/TreeNodeDto.java @@ -0,0 +1,15 @@ +package io.github.linpeilie.model; + +import io.github.linpeilie.annotations.AutoMapper; +import java.util.List; +import lombok.Data; + +@Data +@AutoMapper(target = TreeNode.class, cycleAvoiding = true) +public class TreeNodeDto { + + private TreeNodeDto parent; + + private List children; + +} 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 5715448..21b45ad 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 @@ -189,7 +189,7 @@ public abstract class AbstractAdapterMapperGenerator { // default impl List defaultMethods = buildDefaultProxyMethod(adapterMethodMetadata, - ClassName.get("io.github.linpeilie.annotations", "DoIgnore")); + ClassName.get(ContextConstants.DoIgnore.packageName, ContextConstants.DoIgnore.className)); List methodSpecs = new ArrayList<>(defaultMethods); ParameterSpec parameterSpec = 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 index 7d9eb4f..4c6fcc0 100644 --- 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 @@ -104,4 +104,9 @@ public interface ContextConstants { String className = "MapObjectConvert"; } + interface DoIgnore { + String packageName = "io.github.linpeilie.annotations"; + String className = "DoIgnore"; + } + } 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 b0dab69..2902e4a 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 @@ -11,6 +11,7 @@ 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 io.github.linpeilie.utils.ArrayUtil; import io.github.linpeilie.utils.CollectionUtils; import io.github.linpeilie.utils.StrUtil; import java.io.IOException; @@ -114,6 +115,7 @@ public class AutoMapperGenerator { final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodName) .addParameters(parameterSpecs) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addAnnotation(ClassName.get(ContextConstants.DoIgnore.packageName, ContextConstants.DoIgnore.className)) .returns(target); if (CollectionUtils.isNotEmpty(autoMappingMetadataList)) { methodSpecBuilder.addAnnotations(buildMappingAnnotations(autoMappingMetadataList)); diff --git a/mapstruct-plus/src/main/java/io/github/linpeilie/utils/ArrayUtil.java b/mapstruct-plus/src/main/java/io/github/linpeilie/utils/ArrayUtil.java new file mode 100644 index 0000000..524ed57 --- /dev/null +++ b/mapstruct-plus/src/main/java/io/github/linpeilie/utils/ArrayUtil.java @@ -0,0 +1,23 @@ +package io.github.linpeilie.utils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class ArrayUtil { + + public static boolean isNotEmpty(T[] arr) { + return arr != null && arr.length > 0; + } + + public static String join(T[] arr, CharSequence delimiter) { + return join(arr, delimiter, "", ""); + } + + public static String join(T[] arr, CharSequence delimiter, String prefix, String suffix) { + if (arr == null) { + return null; + } + return Arrays.stream(arr).map(str -> prefix + str + suffix).collect(Collectors.joining(delimiter)); + } + +} diff --git a/pom.xml b/pom.xml index c6c0fb1..ca1de3a 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ - 1.3.7-SNAPSHOTS + 1.4.0 8 8 UTF-8 -- Gitee From 155575096c0e5cc6e6eeb9f932e69004251a188b Mon Sep 17 00:00:00 2001 From: linpeilie Date: Fri, 15 Mar 2024 18:05:20 +0800 Subject: [PATCH 12/12] release 1.4.0-R1 --- docs/.vuepress/config/zh/series.ts | 1 + docs/README.md | 12 ++- docs/en/README.md | 12 ++- docs/en/release/log.md | 8 ++ docs/guide/cycle-avoiding.md | 82 +++++++++++++++++++ docs/guide/faq.md | 2 +- docs/guide/map-to-class.md | 18 ++++ docs/release/log.md | 8 ++ example/pom.xml | 2 +- .../io/github/linpeilie/mapper/Titles.java | 27 ++++++ .../linpeilie/model/EnglishRelease.java | 15 ++++ .../github/linpeilie/model/FrenchRelease.java | 15 ++++ .../io/github/linpeilie/QuickStartTest.java | 49 +++++++++++ pom.xml | 2 +- 14 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 docs/guide/cycle-avoiding.md create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/mapper/Titles.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/EnglishRelease.java create mode 100644 example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/FrenchRelease.java diff --git a/docs/.vuepress/config/zh/series.ts b/docs/.vuepress/config/zh/series.ts index 3fd5110..1d63d92 100644 --- a/docs/.vuepress/config/zh/series.ts +++ b/docs/.vuepress/config/zh/series.ts @@ -23,6 +23,7 @@ export const series = { { text: 'Map 转对象', link: '/guide/map-to-class' }, { text: '枚举转换', link: '/guide/enum-convert' }, { text: '一个类与多个类之间转换', link: '/guide/multiple-class-convert' }, + { text: '类循环嵌套', link: '/guide/cycle-avoiding' }, { text: '类转换API', link: '/guide/converter-api' }, { text: '配置项', link: '/guide/configuration' }, { text: '常见问题', link: '/guide/faq' }, diff --git a/docs/README.md b/docs/README.md index 7682197..41b8007 100644 --- a/docs/README.md +++ b/docs/README.md @@ -62,18 +62,26 @@ footer: io.github.linpeilie mapstruct-plus-spring-boot-starter - 1.3.6 + 1.4.0-R1 ``` - gradle ```groovy -implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.3.6' +implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.4.0-R1' ``` ## 更新日志 +### 1.4.0 + +- **优化复杂对象转换逻辑,占用元空间更小!性能更快!** +- 去除 hutool 等依赖,目前项目中只依赖了 MapStruct +- 适配对象循环嵌套场景 +- [feature#63](https://github.com/linpeilie/mapstruct-plus/pull/63)`AutoMapping`、`ReverseAutoMapping` 支持 `qualifiedByName`、`conditionQualifiedByName` 和 `dependsOn` 属性 +- [issue#I93Z2Z](https://gitee.com/easii/mapstruct-plus/issues/I93Z2Z)`AutoMappings` 支持配置在方法上面 + ### 1.3.6 - 兼容内部类转换 diff --git a/docs/en/README.md b/docs/en/README.md index f2a03a2..403c0b2 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -58,18 +58,26 @@ fotter: io.github.linpeilie mapstruct-plus-spring-boot-starter - 1.3.6 + 1.4.0-R1 ``` - gradle ```groovy -implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.3.6' +implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.4.0-R1' ``` ## Change Log +### 1.4.0 + +- **Optimize complex object conversion logic, take up less meta-space! and faster!** +- Get rid of dependencies such as hutool, which currently only rely on MapStruct in the project. +- The adaptation object loop nesting scenario +- [feature#63](https://github.com/linpeilie/mapstruct-plus/pull/63) `AutoMapping`、`ReverseAutoMapping` supports `qualifiedByName`,`conditionQualifiedByName`,and `dependsOn` properties. +- [issue#I93Z2Z](https://gitee.com/easii/mapstruct-plus/issues/I93Z2Z) `AutoMappings` supports configuration on methods. + ### 1.3.6 - Compatible with internal class conversion. diff --git a/docs/en/release/log.md b/docs/en/release/log.md index 8b97ba6..16e2c73 100644 --- a/docs/en/release/log.md +++ b/docs/en/release/log.md @@ -6,6 +6,14 @@ category: description: MapStructPlus release log --- +### 1.4.0 + +- **Optimize complex object conversion logic, take up less meta-space! and faster!** +- Get rid of dependencies such as hutool, which currently only rely on MapStruct in the project. +- The adaptation object loop nesting scenario +- [feature#63](https://github.com/linpeilie/mapstruct-plus/pull/63) `AutoMapping`、`ReverseAutoMapping` supports `qualifiedByName`,`conditionQualifiedByName`,and `dependsOn` properties. +- [issue#I93Z2Z](https://gitee.com/easii/mapstruct-plus/issues/I93Z2Z) `AutoMappings` supports configuration on methods. + ### 1.3.6 - Compatible with internal class conversion. diff --git a/docs/guide/cycle-avoiding.md b/docs/guide/cycle-avoiding.md new file mode 100644 index 0000000..23d8c63 --- /dev/null +++ b/docs/guide/cycle-avoiding.md @@ -0,0 +1,82 @@ +--- +title: 类循环嵌套 +order: 7 +category: +- 指南 +description: MapStructPlus MapStructPlus类循环嵌套 CycleAvoiding +--- + +## 背景 + +类循环嵌套是指两个类互相引用,例如,源对象和目标对象结构都包含父对象和子对象之间的双向关联。 +当存在这种情况时,直接进行转换时,会导致栈溢出的问题(stack overflow error)。 + +示例: + +```java +@Data +public class TreeNode { + private TreeNode parent; + private List children; +} + +@Data +public class TreeNodeDto { + private TreeNodeDto parent; + private List children; +} +``` + +`parent` 属性可以是其他类型的,可能跨越一个更长的属性链形成的嵌套循环。 + +为了适配这种情况,MapStructPlus 的 **`AutoMapper`** 注解中增加了 **`cycleAvoiding`** 属性,该属性用于标识,是否需要避免循环嵌套的问题。 +默认为 `false`,如果需要避免循环嵌套,需要将该属性设置为 `true`。 + +当配置为 `true` 时,在整个对象的转换过程链路中,会传递一个 `CycleAvoidingMappingContext` 对象,临时保存转换生成的对象, +在转换链路中,如果发现需要生成的对象已经存在,会直接返回该类型,从而避免栈溢出问题。 +所以,配置该属性为 `true` 时,会有一点的性能消耗,如果没有循环嵌套的情况,使用默认配置即可,避免不必要的性能消耗。 + +## 使用示例 + +以上面的示例为例,在 `AutoMapper` 注解中,配置 `cycleAvoiding` 属性为 `true`,如下所示: + +```java +@Data +@AutoMapper(target = TreeNodeDto.class, cycleAvoiding = true) +public class TreeNode { + private TreeNode parent; + private List children; +} + +@Data +@AutoMapper(target = TreeNode.class, cycleAvoiding = true) +public class TreeNodeDto { + private TreeNodeDto parent; + private List children; +} +``` + +编译生成的转换逻辑如下: + +```java +public TreeNodeDto convert(TreeNode arg0, CycleAvoidingMappingContext arg1) { + TreeNodeDto target = arg1.getMappedInstance(arg0, TreeNodeDto.class); + if (target != null) { + return target; + } + + if (arg0 == null) { + return null; + } + + TreeNodeDto treeNodeDto = new TreeNodeDto(); + + arg1.storeMappedInstance(arg0, treeNodeDto); + + treeNodeDto.setParent(demoConvertMapperAdapterForCycleAvoiding.iglm_TreeNodeToTreeNodeDto(arg0.getParent(), arg1)); + treeNodeDto.setChildren( + demoConvertMapperAdapterForCycleAvoiding.iglm_TreeNodeToTreeNodeDto(arg0.getChildren(), arg1)); + + return treeNodeDto; +} +``` \ No newline at end of file diff --git a/docs/guide/faq.md b/docs/guide/faq.md index 41b257c..f08f39e 100644 --- a/docs/guide/faq.md +++ b/docs/guide/faq.md @@ -1,6 +1,6 @@ --- title: 常见问题 -order: 7 +order: 8 category: - 指南 description: MapStructPlus MapStructPlus常见问题 faq diff --git a/docs/guide/map-to-class.md b/docs/guide/map-to-class.md index 6a80b3a..7bb1dfa 100644 --- a/docs/guide/map-to-class.md +++ b/docs/guide/map-to-class.md @@ -8,8 +8,26 @@ description: MapStructPlus Map转为对象 map convert to class MapStructPlus 提供了更加强大的 `Map` 转对象的功能。 +::: warning +MapStructPlus 1.4.0 及以后版本,不再内置 [Hutool](https://hutool.cn) 框架,如果需要用到该功能时,需要额外引入 `hutool-core` 依赖。 +::: + ## 使用 +### 添加依赖 + +> 1.4.0 及以后的版本需要添加该依赖,1.4.0之前的版本内置 hutool,不需要额外添加。 + +```xml + + cn.hutool + hutool-core + ${hutool.version} + +``` + +### 添加注解 + **当想要自动生成 `Map` 转为目标类的接口及实现类时,只需要在目标类上添加 `@AutoMapMapper` 注解**。 ## 支持的 value 类型 diff --git a/docs/release/log.md b/docs/release/log.md index 175f8fe..cf7dc5d 100644 --- a/docs/release/log.md +++ b/docs/release/log.md @@ -6,6 +6,14 @@ category: description: MapStructPlus release log --- +### 1.4.0 + +- **优化复杂对象转换逻辑,占用元空间更小!性能更快!** +- 去除 hutool 等依赖,目前项目中只依赖了 MapStruct +- 适配对象循环嵌套场景 +- [feature#63](https://github.com/linpeilie/mapstruct-plus/pull/63)`AutoMapping`、`ReverseAutoMapping` 支持 `qualifiedByName`、`conditionQualifiedByName` 和 `dependsOn` 属性 +- [issue#I93Z2Z](https://gitee.com/easii/mapstruct-plus/issues/I93Z2Z)`AutoMappings` 支持配置在方法上面 + ### 1.3.6 - 兼容内部类转换 diff --git a/example/pom.xml b/example/pom.xml index 40528de..a16c8ff 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -18,7 +18,7 @@ UTF-8 1.5.1.Final - 1.4.0 + 1.4.0-R1 1.18.22 5.8.26 diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/mapper/Titles.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/mapper/Titles.java new file mode 100644 index 0000000..a30b35d --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/mapper/Titles.java @@ -0,0 +1,27 @@ +package io.github.linpeilie.mapper; + +import org.mapstruct.Named; +import org.springframework.boot.context.properties.bind.Name; +import org.springframework.stereotype.Component; + +@Component +@Named("TitleTranslator") +public class Titles { + + @Named("EnglishToFrench") + public String translateTitleEF(String title) { + if ("One Hundred Years of Solitude".equals(title)) { + return "Cent ans de solitude"; + } + return "Inconnu et inconnu"; + } + + @Named("FrenchToEnglish") + public String translateTitleFE(String title) { + if ("Cent ans de solitude".equals(title)) { + return "One Hundred Years of Solitude"; + } + return "Unknown"; + } + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/EnglishRelease.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/EnglishRelease.java new file mode 100644 index 0000000..87f5082 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/EnglishRelease.java @@ -0,0 +1,15 @@ +package io.github.linpeilie.model; + +import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.AutoMapping; +import io.github.linpeilie.mapper.Titles; +import lombok.Data; + +@Data +@AutoMapper(target = FrenchRelease.class, uses = Titles.class) +public class EnglishRelease { + + @AutoMapping(qualifiedByName = "EnglishToFrench") + private String title; + +} diff --git a/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/FrenchRelease.java b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/FrenchRelease.java new file mode 100644 index 0000000..fdbd4e3 --- /dev/null +++ b/example/spring-boot-with-lombok/src/main/java/io/github/linpeilie/model/FrenchRelease.java @@ -0,0 +1,15 @@ +package io.github.linpeilie.model; + +import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.AutoMapping; +import io.github.linpeilie.mapper.Titles; +import lombok.Data; + +@Data +@AutoMapper(target = EnglishRelease.class, uses = Titles.class) +public class FrenchRelease { + + @AutoMapping(qualifiedByName = "FrenchToEnglish") + private String title; + +} diff --git a/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/QuickStartTest.java b/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/QuickStartTest.java index 7ef1639..ee90bba 100644 --- a/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/QuickStartTest.java +++ b/example/spring-boot-with-lombok/src/test/java/io/github/linpeilie/QuickStartTest.java @@ -2,7 +2,10 @@ package io.github.linpeilie; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; import io.github.linpeilie.model.Car; +import io.github.linpeilie.model.EnglishRelease; +import io.github.linpeilie.model.FrenchRelease; import io.github.linpeilie.model.Goods; import io.github.linpeilie.model.GoodsDto; import io.github.linpeilie.model.GoodsStateEnum; @@ -16,6 +19,8 @@ import io.github.linpeilie.model.SVO; import io.github.linpeilie.model.Sku; import io.github.linpeilie.model.SysMenu; import io.github.linpeilie.model.SysMenuVo; +import io.github.linpeilie.model.TreeNode; +import io.github.linpeilie.model.TreeNodeDto; import io.github.linpeilie.model.User; import io.github.linpeilie.model.UserDto; import io.github.linpeilie.model.UserVO; @@ -262,4 +267,48 @@ public class QuickStartTest { System.out.println(sDto1); } + @Test + public void testConditionQualifiedByName() { + TreeNode parent = new TreeNode(); + // children + List children = new ArrayList<>(); + for (int i = 0; i < 1; i++) { + TreeNode child = new TreeNode(); + child.setParent(parent); + children.add(child); + } + parent.setChildren(children); + TreeNodeDto treeNodeDto1 = converter.convert(parent, TreeNodeDto.class); + // 当 children 为 <2 时不进行转换 + Assert.isNull(treeNodeDto1.getChildren()); + for (int i = 0; i < 99; i++) { + TreeNode child = new TreeNode(); + child.setParent(parent); + children.add(child); + } + TreeNodeDto treeNodeDto2 = converter.convert(parent, TreeNodeDto.class); + Assert.equals(treeNodeDto2.getChildren().size(), 100); + + treeNodeDto2.getChildren().forEach(child -> { + Assert.equals(child.getParent(), treeNodeDto2); + }); + } + + @Test + public void testQualifiedByName() { + EnglishRelease englishRelease = new EnglishRelease(); + englishRelease.setTitle("Algorithms, 4th Edition"); + FrenchRelease frenchRelease1 = converter.convert(englishRelease, FrenchRelease.class); + Assert.equals(frenchRelease1.getTitle(), "Inconnu et inconnu"); + englishRelease.setTitle("One Hundred Years of Solitude"); + FrenchRelease frenchRelease2 = converter.convert(englishRelease, FrenchRelease.class); + Assert.equals(frenchRelease2.getTitle(), "Cent ans de solitude"); + + EnglishRelease englishRelease1 = converter.convert(frenchRelease1, EnglishRelease.class); + Assert.equals(englishRelease1.getTitle(), "Unknown"); + frenchRelease2.setTitle("Cent ans de solitude"); + EnglishRelease englishRelease2 = converter.convert(frenchRelease2, EnglishRelease.class); + Assert.equals(englishRelease2.getTitle(), "One Hundred Years of Solitude"); + } + } diff --git a/pom.xml b/pom.xml index ca1de3a..904ab45 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ - 1.4.0 + 1.4.0-R1 8 8 UTF-8 -- Gitee