mirror of
https://gitee.com/easii/mapstruct-plus.git
synced 2025-12-07 01:28:31 +08:00
commit
3a9206cdeb
@ -61,7 +61,7 @@ public class User {
|
|||||||
|
|
||||||
```xml
|
```xml
|
||||||
<properties>
|
<properties>
|
||||||
<mapstruct-plus.version>1.3.1</mapstruct-plus.version>
|
<mapstruct-plus.version>1.3.2</mapstruct-plus.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@ -56,27 +56,22 @@ copyright: false
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.linpeilie</groupId>
|
<groupId>io.github.linpeilie</groupId>
|
||||||
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
|
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
|
||||||
<version>1.3.1</version>
|
<version>1.3.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
- gradle
|
- gradle
|
||||||
|
|
||||||
```groovy
|
```groovy
|
||||||
implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.3.1'
|
implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.3.2'
|
||||||
```
|
```
|
||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
|
||||||
### 1.3.1
|
### 1.3.2
|
||||||
|
|
||||||
- 增加编译参数中指定配置类的功能
|
- 不可变对象支持,可以使用任意包下的 `Immutable` 标注类型为不可变类
|
||||||
- 更好地适配 IDEA 部分编译场景
|
- 全面适配 IDEA 部分编译问题,使用更加流畅丝滑
|
||||||
|
|
||||||
### 1.3.0
|
|
||||||
|
|
||||||
- fix: 解决本地开发时 IDEA 编译或者运行时报错等与预期不一致的问题
|
|
||||||
- feature: AutoMapper 注解增加 imports 属性支持
|
|
||||||
|
|
||||||
……
|
……
|
||||||
|
|
||||||
|
|||||||
@ -514,3 +514,17 @@ public class CarDtoToCarMapperImpl implements CarDtoToCarMapper {
|
|||||||
|
|
||||||
如果还是不理解,这里可以认为,该注解就是本该应用在目标类中的 `@AutoMapping` 注解,原封不动拷贝到当前类,再修改注解名称即可。
|
如果还是不理解,这里可以认为,该注解就是本该应用在目标类中的 `@AutoMapping` 注解,原封不动拷贝到当前类,再修改注解名称即可。
|
||||||
|
|
||||||
|
## 不可变类型设计
|
||||||
|
|
||||||
|
> since 1.3.2
|
||||||
|
|
||||||
|
当一个类型是不可变类型时,之前默认的规则,生成的 `T convert(S source, @MappingTarget T target)` 可能会存在问题。
|
||||||
|
|
||||||
|
所以,可以使用任意包下的 `Immutable` 注解,标识一个类为不可变类型,
|
||||||
|
当为不可变类型时,`@MappingTarget` 没有意义,上面的方法最终生成如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public T convert(S source, @MappingTarget T target) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
```
|
||||||
@ -23,10 +23,13 @@ public class MapStructPlusConfiguration {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
:::warning
|
:::info
|
||||||
**当使用该方式配置时,强烈建议,在编译参数中,指定配置类为当前类,以解决IDEA部分编译场景时出现的各种问题,该功能从 1.3.1 开始支持**
|
|
||||||
|
|
||||||
配置时,需要在启动参数中添加 `-Amapstruct.plus.mapperConfigClass` 参数,该参数的值为配置类的全路径名称:
|
1.3.2 已彻底适配 IDEA 部分编译,无需再添加如下配置。
|
||||||
|
|
||||||
|
~~当使用该方式配置时,强烈建议,在编译参数中,指定配置类为当前类,以解决IDEA部分编译场景时出现的各种问题,该功能从 1.3.1 开始支持
|
||||||
|
|
||||||
|
配置时,需要在启动参数中添加 `-Amapstruct.plus.mapperConfigClass` 参数,该参数的值为配置类的全路径名称~~:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|||||||
@ -6,6 +6,11 @@ category:
|
|||||||
description: MapStructPlus release log
|
description: MapStructPlus release log
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 1.3.2
|
||||||
|
|
||||||
|
- 不可变对象支持,可以使用任意包下的 `Immutable` 标注类型为不可变类
|
||||||
|
- 全面适配 IDEA 部分编译问题,使用更加流畅丝滑
|
||||||
|
|
||||||
### 1.3.1
|
### 1.3.1
|
||||||
|
|
||||||
- 增加编译参数中指定配置类的功能
|
- 增加编译参数中指定配置类的功能
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<mapstruct.version>1.5.1.Final</mapstruct.version>
|
<mapstruct.version>1.5.1.Final</mapstruct.version>
|
||||||
<mapstruct-plus.version>1.3.1</mapstruct-plus.version>
|
<mapstruct-plus.version>1.3.2</mapstruct-plus.version>
|
||||||
<lombok.version>1.18.22</lombok.version>
|
<lombok.version>1.18.22</lombok.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|||||||
@ -20,4 +20,14 @@ public class Goods {
|
|||||||
|
|
||||||
private GoodsTypeEnum type;
|
private GoodsTypeEnum type;
|
||||||
|
|
||||||
|
private String f1;
|
||||||
|
|
||||||
|
private String f2;
|
||||||
|
|
||||||
|
private String f3;
|
||||||
|
|
||||||
|
private String f4;
|
||||||
|
|
||||||
|
private String f5;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,4 +19,14 @@ public class GoodsDto {
|
|||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
|
|
||||||
|
private String f1;
|
||||||
|
|
||||||
|
private String f2;
|
||||||
|
|
||||||
|
private String f3;
|
||||||
|
|
||||||
|
private String f4;
|
||||||
|
|
||||||
|
private String f5;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,12 +23,6 @@ public abstract class AbstractAdapterMapperGenerator {
|
|||||||
public void write(ProcessingEnvironment processingEnv,
|
public void write(ProcessingEnvironment processingEnv,
|
||||||
Collection<AbstractAdapterMethodMetadata> adapterMethods,
|
Collection<AbstractAdapterMethodMetadata> adapterMethods,
|
||||||
String adapterClassName) {
|
String adapterClassName) {
|
||||||
final TypeElement typeElement =
|
|
||||||
processingEnv.getElementUtils().getTypeElement(adapterPackage() + "." + adapterClassName);
|
|
||||||
if (typeElement != null) {
|
|
||||||
System.out.println("adapter class existed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// write Adapter
|
// write Adapter
|
||||||
try (final Writer writer = processingEnv.getFiler()
|
try (final Writer writer = processingEnv.getFiler()
|
||||||
.createSourceFile(adapterPackage() + "." + adapterClassName)
|
.createSourceFile(adapterPackage() + "." + adapterClassName)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package io.github.linpeilie.processor;
|
package io.github.linpeilie.processor;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.squareup.javapoet.ClassName;
|
import com.squareup.javapoet.ClassName;
|
||||||
import com.squareup.javapoet.TypeName;
|
import com.squareup.javapoet.TypeName;
|
||||||
@ -39,10 +40,14 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.processing.AbstractProcessor;
|
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;
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||||
import javax.annotation.processing.SupportedOptions;
|
import javax.annotation.processing.SupportedOptions;
|
||||||
@ -55,7 +60,6 @@ import javax.lang.model.type.MirroredTypeException;
|
|||||||
import javax.lang.model.type.MirroredTypesException;
|
import javax.lang.model.type.MirroredTypesException;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
import javax.tools.Diagnostic;
|
import javax.tools.Diagnostic;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.mapstruct.MappingConstants;
|
import org.mapstruct.MappingConstants;
|
||||||
|
|
||||||
import static io.github.linpeilie.processor.Constants.AUTO_ENUM_MAPPER_ANNOTATION;
|
import static io.github.linpeilie.processor.Constants.AUTO_ENUM_MAPPER_ANNOTATION;
|
||||||
@ -65,8 +69,18 @@ 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.COMPONENT_MODEL_CONFIG_ANNOTATION;
|
||||||
import static io.github.linpeilie.processor.Constants.MAPPER_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.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;
|
||||||
|
import static io.github.linpeilie.processor.ProcessorOptions.BUILDER_DISABLE_BUILDER;
|
||||||
|
import static io.github.linpeilie.processor.ProcessorOptions.MAPPER_CONFIG_CLASS;
|
||||||
|
import static io.github.linpeilie.processor.ProcessorOptions.MAPPER_PACKAGE;
|
||||||
|
import static io.github.linpeilie.processor.ProcessorOptions.MAP_ADAPTER_CLASS_NAME;
|
||||||
|
import static io.github.linpeilie.processor.ProcessorOptions.NULL_VALUE_MAPPING_STRATEGY;
|
||||||
|
import static io.github.linpeilie.processor.ProcessorOptions.NULL_VALUE_PROPERTY_MAPPING_STRATEGY;
|
||||||
|
import static io.github.linpeilie.processor.ProcessorOptions.UNMAPPED_SOURCE_POLICY;
|
||||||
|
import static io.github.linpeilie.processor.ProcessorOptions.UNMAPPED_TARGET_POLICY;
|
||||||
import static javax.tools.Diagnostic.Kind.ERROR;
|
import static javax.tools.Diagnostic.Kind.ERROR;
|
||||||
import static io.github.linpeilie.processor.ProcessorOptions.*;
|
|
||||||
|
|
||||||
@SupportedAnnotationTypes({AUTO_MAPPER_ANNOTATION, AUTO_MAPPERS_ANNOTATION, AUTO_MAP_MAPPER_ANNOTATION,
|
@SupportedAnnotationTypes({AUTO_MAPPER_ANNOTATION, AUTO_MAPPERS_ANNOTATION, AUTO_MAP_MAPPER_ANNOTATION,
|
||||||
AUTO_ENUM_MAPPER_ANNOTATION, MAPPER_CONFIG_ANNOTATION, COMPONENT_MODEL_CONFIG_ANNOTATION,
|
AUTO_ENUM_MAPPER_ANNOTATION, MAPPER_CONFIG_ANNOTATION, COMPONENT_MODEL_CONFIG_ANNOTATION,
|
||||||
@ -104,6 +118,10 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
|
|
||||||
private final Set<String> mapperSet = new HashSet<>();
|
private final Set<String> mapperSet = new HashSet<>();
|
||||||
|
|
||||||
|
private Messager messager;
|
||||||
|
|
||||||
|
private Filer filer;
|
||||||
|
|
||||||
public AutoMapperProcessor() {
|
public AutoMapperProcessor() {
|
||||||
this.mapperGenerator = new AutoMapperGenerator();
|
this.mapperGenerator = new AutoMapperGenerator();
|
||||||
this.mapperConfigGenerator = new MapperConfigGenerator();
|
this.mapperConfigGenerator = new MapperConfigGenerator();
|
||||||
@ -137,14 +155,32 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
return COMPONENT_MODEL_CONFIG_ANNOTATION.contentEquals(annotation.getQualifiedName());
|
return COMPONENT_MODEL_CONFIG_ANNOTATION.contentEquals(annotation.getQualifiedName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void init(final ProcessingEnvironment processingEnv) {
|
||||||
|
super.init(processingEnv);
|
||||||
|
messager = processingEnv.getMessager();
|
||||||
|
filer = processingEnv.getFiler();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
|
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
|
||||||
|
try {
|
||||||
|
doProcess(annotations, roundEnv);
|
||||||
|
} catch (Exception e) {
|
||||||
|
messager.printMessage(ERROR, ExceptionUtil.stacktraceToString(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doProcess(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
|
||||||
boolean hasAutoMapper = annotations.stream().anyMatch(this::isAutoMapperAnnotation);
|
boolean hasAutoMapper = annotations.stream().anyMatch(this::isAutoMapperAnnotation);
|
||||||
final boolean hasAutoMapMapper = annotations.stream().anyMatch(this::isAutoMapMapperAnnotation);
|
final boolean hasAutoMapMapper = annotations.stream().anyMatch(this::isAutoMapMapperAnnotation);
|
||||||
final boolean hasAutoEnumMapper = annotations.stream().anyMatch(this::isAutoEnumMapperAnnotation);
|
final boolean hasAutoEnumMapper = annotations.stream().anyMatch(this::isAutoEnumMapperAnnotation);
|
||||||
final boolean hasAutoMapMappers = annotations.stream().anyMatch(this::isAutoMappersAnnotation);
|
final boolean hasAutoMapMappers = annotations.stream().anyMatch(this::isAutoMappersAnnotation);
|
||||||
if (!hasAutoMapper && !hasAutoMapMapper && !hasAutoEnumMapper && !hasAutoMapMappers) {
|
final boolean hasMapperConfig = annotations.stream().anyMatch(this::isMapperConfigAnnotation);
|
||||||
return false;
|
if (!hasAutoMapper && !hasAutoMapMapper && !hasAutoEnumMapper && !hasAutoMapMappers && !hasMapperConfig) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// 刷新配置
|
// 刷新配置
|
||||||
refreshProperties(annotations, roundEnv);
|
refreshProperties(annotations, roundEnv);
|
||||||
@ -162,49 +198,57 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AutoMapMapper
|
// AutoMapMapper
|
||||||
annotations.stream()
|
final TypeElement autoMapMapperAnnotation =
|
||||||
.filter(this::isAutoMapMapperAnnotation)
|
processingEnv.getElementUtils().getTypeElement(AUTO_MAP_MAPPER_ANNOTATION);
|
||||||
.findFirst()
|
processAutoMapMapperAnnotation(roundEnv, autoMapMapperAnnotation);
|
||||||
.ifPresent(annotation -> processAutoMapMapperAnnotation(roundEnv, annotation));
|
|
||||||
|
|
||||||
// AutoEnumMapper
|
// AutoEnumMapper
|
||||||
annotations.stream()
|
final TypeElement autoEnumMapperAnnotation =
|
||||||
.filter(this::isAutoEnumMapperAnnotation)
|
processingEnv.getElementUtils().getTypeElement(AUTO_ENUM_MAPPER_ANNOTATION);
|
||||||
.findFirst()
|
processAutoEnumMapperAnnotation(roundEnv, autoEnumMapperAnnotation);
|
||||||
.ifPresent(annotation -> processAutoEnumMapperAnnotation(roundEnv, annotation));
|
|
||||||
|
|
||||||
// AutoMapper
|
// AutoMapper
|
||||||
annotations.stream()
|
final TypeElement autoMapperAnnotation = processingEnv.getElementUtils().getTypeElement(AUTO_MAPPER_ANNOTATION);
|
||||||
.filter(this::isAutoMapperAnnotation)
|
processAutoMapperAnnotation(roundEnv, autoMapperAnnotation);
|
||||||
.findFirst()
|
|
||||||
.ifPresent(annotation -> processAutoMapperAnnotation(roundEnv, annotation));
|
|
||||||
|
|
||||||
// AutoMappers
|
// AutoMappers
|
||||||
annotations.stream()
|
final TypeElement autoMappersAnnotation =
|
||||||
.filter(this::isAutoMappersAnnotation)
|
processingEnv.getElementUtils().getTypeElement(AUTO_MAPPERS_ANNOTATION);
|
||||||
.findFirst()
|
processAutoMappersAnnotation(roundEnv, autoMappersAnnotation);
|
||||||
.ifPresent(annotation -> processAutoMappersAnnotation(roundEnv, annotation));
|
|
||||||
|
|
||||||
// custom mapper
|
// custom mapper
|
||||||
annotations.stream()
|
final TypeElement mapperAnnotation = processingEnv.getElementUtils().getTypeElement(MAPPER_ANNOTATION);
|
||||||
.filter(this::isMapperAnnotation)
|
processMapperAnnotation(roundEnv, mapperAnnotation);
|
||||||
.findFirst()
|
|
||||||
.ifPresent(annotation -> processMapperAnnotation(roundEnv, annotation));
|
|
||||||
|
|
||||||
// 生成类
|
// 生成类
|
||||||
generateMapper();
|
generateMapper();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
private List<TypeElement> getElementAndMergeHistory(final RoundEnvironment roundEnv,
|
||||||
|
TypeElement annotation,
|
||||||
|
BuildCollator buildCollator) {
|
||||||
|
buildCollator.appendNonexistent(roundEnv.getElementsAnnotatedWith(annotation));
|
||||||
|
return buildCollator.getRecords();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
private void processMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||||
roundEnv.getElementsAnnotatedWith(annotation)
|
if (annotation == null) {
|
||||||
.forEach(element -> customMapperList.add(element.asType()));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
|
||||||
|
new BuildCollator(processingEnv, Constants.MAPPERS_FILE_NAME));
|
||||||
|
|
||||||
|
elements.forEach(element -> customMapperList.add(element.asType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processAutoEnumMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
private void processAutoEnumMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||||
roundEnv.getElementsAnnotatedWith(annotation)
|
if (annotation == null) {
|
||||||
.stream()
|
return;
|
||||||
|
}
|
||||||
|
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
|
||||||
|
new BuildCollator(processingEnv, Constants.ENUM_MAPPERS_FILE_NAME));
|
||||||
|
elements.stream()
|
||||||
.map(this::buildAutoEnumMapperMetadata)
|
.map(this::buildAutoEnumMapperMetadata)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.forEach(this::writeAutoEnumMapperFile);
|
.forEach(this::writeAutoEnumMapperFile);
|
||||||
@ -225,6 +269,9 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addAdapterMethodMetadata(final AutoEnumMapperMetadata autoEnumMapperMetadata) {
|
private void addAdapterMethodMetadata(final AutoEnumMapperMetadata autoEnumMapperMetadata) {
|
||||||
|
if (autoEnumMapperMetadata == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// toValue
|
// toValue
|
||||||
final AdapterEnumMethodMetadata toValueProxyMethod =
|
final AdapterEnumMethodMetadata toValueProxyMethod =
|
||||||
new AdapterEnumMethodMetadata(autoEnumMapperMetadata.getSourceClassName(),
|
new AdapterEnumMethodMetadata(autoEnumMapperMetadata.getSourceClassName(),
|
||||||
@ -237,7 +284,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
ClassName.get(autoEnumMapperMetadata.mapperPackage(), autoEnumMapperMetadata.mapperName()),
|
ClassName.get(autoEnumMapperMetadata.mapperPackage(), autoEnumMapperMetadata.mapperName()),
|
||||||
autoEnumMapperMetadata.toEnumMethodName(),
|
autoEnumMapperMetadata.toEnumMethodName(),
|
||||||
autoEnumMapperMetadata.getSourceClassName());
|
autoEnumMapperMetadata.getSourceClassName());
|
||||||
methodMap.put(
|
methodMap.putIfAbsent(
|
||||||
autoEnumMapperMetadata.getSourceClassName().simpleName() + toValueProxyMethod.getMapperMethodName(),
|
autoEnumMapperMetadata.getSourceClassName().simpleName() + toValueProxyMethod.getMapperMethodName(),
|
||||||
toValueProxyMethod);
|
toValueProxyMethod);
|
||||||
methodMap.put(
|
methodMap.put(
|
||||||
@ -278,13 +325,34 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processAutoMapMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
private void processAutoMapMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||||
final List<AutoMapperMetadata> autoMapMapperMetadataList =
|
if (annotation == null) {
|
||||||
roundEnv.getElementsAnnotatedWith(annotation).stream().map(ele -> {
|
return;
|
||||||
if (ele.getAnnotation(AutoMapMapper.class) == null) {
|
}
|
||||||
|
|
||||||
|
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
|
||||||
|
new BuildCollator(processingEnv, Constants.AUTO_MAP_MAPPERS_FILE_NAME));
|
||||||
|
|
||||||
|
elements.stream()
|
||||||
|
.map(ele -> buildAutoMapMapperMetadata((TypeElement) ele))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(metadata -> {
|
||||||
|
this.writeAutoMapperClassFile(metadata);
|
||||||
|
addAdapterMapMethod(metadata);
|
||||||
|
});
|
||||||
|
|
||||||
|
adapterMapperGenerator.write(processingEnv, mapMethodMap.values(),
|
||||||
|
AutoMapperProperties.getMapAdapterClassName());
|
||||||
|
|
||||||
|
mapperConfigGenerator.write(processingEnv, AutoMapperProperties.getMapConfigClassName(),
|
||||||
|
AutoMapperProperties.getMapAdapterClassName(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AutoMapperMetadata buildAutoMapMapperMetadata(TypeElement element) {
|
||||||
|
if (element.getAnnotation(AutoMapMapper.class) == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ClassName source = ClassName.get("java.util", "Map");
|
ClassName source = ClassName.get("java.util", "Map");
|
||||||
ClassName target = ClassName.get((TypeElement) ele);
|
ClassName target = ClassName.get(element);
|
||||||
List<ClassName> uses = Arrays.asList(ClassName.get("io.github.linpeilie.map", "MapObjectConvert"));
|
List<ClassName> uses = Arrays.asList(ClassName.get("io.github.linpeilie.map", "MapObjectConvert"));
|
||||||
|
|
||||||
final AutoMapperMetadata autoMapperMetadata = new AutoMapMapperMetadata();
|
final AutoMapperMetadata autoMapperMetadata = new AutoMapMapperMetadata();
|
||||||
@ -296,19 +364,16 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
autoMapperMetadata.setMapstructConfigClass(ClassName.get(AutoMapperProperties.getConfigPackage(),
|
autoMapperMetadata.setMapstructConfigClass(ClassName.get(AutoMapperProperties.getConfigPackage(),
|
||||||
AutoMapperProperties.getMapConfigClassName()));
|
AutoMapperProperties.getMapConfigClassName()));
|
||||||
return autoMapperMetadata;
|
return autoMapperMetadata;
|
||||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
}
|
||||||
autoMapMapperMetadataList.forEach(metadata -> {
|
|
||||||
this.writeAutoMapperClassFile(metadata);
|
private void addAdapterMapMethod(AutoMapperMetadata metadata) {
|
||||||
|
if (metadata == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
addAdapterMapMethod(metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass(),
|
addAdapterMapMethod(metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass(),
|
||||||
false);
|
false);
|
||||||
addAdapterMapMethod(ClassName.get("java.lang", "Object"), metadata.getTargetClassName(),
|
addAdapterMapMethod(ClassName.get("java.lang", "Object"), metadata.getTargetClassName(),
|
||||||
metadata.mapperClass(), true);
|
metadata.mapperClass(), true);
|
||||||
});
|
|
||||||
adapterMapperGenerator.write(processingEnv, mapMethodMap.values(),
|
|
||||||
AutoMapperProperties.getMapAdapterClassName());
|
|
||||||
|
|
||||||
mapperConfigGenerator.write(processingEnv, AutoMapperProperties.getMapConfigClassName(),
|
|
||||||
AutoMapperProperties.getMapAdapterClassName(), null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadMapperConfig(MapperConfig mapperConfig) {
|
private void loadMapperConfig(MapperConfig mapperConfig) {
|
||||||
@ -336,12 +401,30 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void refreshProperties(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
|
private void refreshProperties(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
|
||||||
|
final BuildCollator buildCollator = new BuildCollator(processingEnv, Constants.MAPPER_CONFIG_FILE_NAME);
|
||||||
|
|
||||||
|
// load previous mapper config
|
||||||
|
final List<TypeElement> typeElements = buildCollator.getRecords();
|
||||||
|
if (CollectionUtil.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));
|
||||||
|
}
|
||||||
|
|
||||||
// annotation --> MapperConfig
|
// annotation --> MapperConfig
|
||||||
annotations.stream()
|
final TypeElement mapperConfigAnnotation =
|
||||||
.filter(this::isMapperConfigAnnotation)
|
processingEnv.getElementUtils().getTypeElement(MAPPER_CONFIG_ANNOTATION);
|
||||||
.findFirst()
|
if (mapperConfigAnnotation != null) {
|
||||||
.flatMap(annotation -> roundEnv.getElementsAnnotatedWith(annotation).stream().findFirst())
|
final Optional<? extends Element> mapperConfigOptional =
|
||||||
.ifPresent(element -> loadMapperConfig(element.getAnnotation(MapperConfig.class)));
|
roundEnv.getElementsAnnotatedWith(mapperConfigAnnotation)
|
||||||
|
.stream()
|
||||||
|
.findFirst();
|
||||||
|
if (mapperConfigOptional.isPresent()) {
|
||||||
|
loadMapperConfig(mapperConfigOptional.get().getAnnotation(MapperConfig.class));
|
||||||
|
// record
|
||||||
|
buildCollator.writeTypeElements(CollectionUtil.newArrayList((TypeElement) mapperConfigOptional.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// special MapperConfig Class
|
// special MapperConfig Class
|
||||||
final String mapperConfigClass = processingEnv.getOptions().get(MAPPER_CONFIG_CLASS);
|
final String mapperConfigClass = processingEnv.getOptions().get(MAPPER_CONFIG_CLASS);
|
||||||
@ -376,7 +459,13 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processAutoMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
private void processAutoMapperAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||||
final List<AutoMapperMetadata> autoMapperMetadataList = roundEnv.getElementsAnnotatedWith(annotation)
|
if (annotation == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final List<TypeElement> elements = getElementAndMergeHistory(roundEnv, annotation,
|
||||||
|
new BuildCollator(processingEnv, Constants.AUTO_MAPPER_FILE_NAME));
|
||||||
|
|
||||||
|
final List<AutoMapperMetadata> autoMapperMetadataList = elements
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::buildAutoMapperMetadata)
|
.map(this::buildAutoMapperMetadata)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
@ -386,7 +475,11 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processAutoMappersAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
private void processAutoMappersAnnotation(final RoundEnvironment roundEnv, final TypeElement annotation) {
|
||||||
final List<AutoMapperMetadata> autoMapperMetadata = roundEnv.getElementsAnnotatedWith(annotation)
|
if (annotation == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final List<AutoMapperMetadata> autoMapperMetadata = getElementAndMergeHistory(roundEnv, annotation,
|
||||||
|
new BuildCollator(processingEnv, Constants.AUTO_MAPPERS_FILE_NAME))
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::buildAutoMapperMetadataByAutoMappers)
|
.map(this::buildAutoMapperMetadataByAutoMappers)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
@ -420,7 +513,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.writeAutoMapperClassFile(metadata);
|
this.writeAutoMapperClassFile(metadata);
|
||||||
addAdapterMethod(metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass());
|
addAdapterMethod(metadata);
|
||||||
});
|
});
|
||||||
|
|
||||||
adapterMapperGenerator.write(processingEnv, methodMap.values(), AutoMapperProperties.getAdapterClassName());
|
adapterMapperGenerator.write(processingEnv, methodMap.values(), AutoMapperProperties.getAdapterClassName());
|
||||||
@ -459,7 +552,7 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
try (final Writer writer = processingEnv.getFiler()
|
try (final Writer writer = processingEnv.getFiler()
|
||||||
.createSourceFile(mapperPackage + "." + mapperClassName)
|
.createSourceFile(mapperPackage + "." + mapperClassName)
|
||||||
.openWriter()) {
|
.openWriter()) {
|
||||||
mapperGenerator.write(metadata, writer);
|
mapperGenerator.write(metadata, processingEnv, writer);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
processingEnv.getMessager()
|
processingEnv.getMessager()
|
||||||
.printMessage(ERROR,
|
.printMessage(ERROR,
|
||||||
@ -467,15 +560,19 @@ public class AutoMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAdapterMethod(ClassName source, ClassName target, ClassName mapper) {
|
private void addAdapterMethod(AutoMapperMetadata metadata) {
|
||||||
AdapterMethodMetadata adapterMethodMetadata = AdapterMethodMetadata.newInstance(source, target, mapper);
|
if (metadata == null) {
|
||||||
methodMap.put(adapterMethodMetadata.getMethodName(), adapterMethodMetadata);
|
return;
|
||||||
|
}
|
||||||
|
AdapterMethodMetadata adapterMethodMetadata = AdapterMethodMetadata.newInstance(
|
||||||
|
metadata.getSourceClassName(), metadata.getTargetClassName(), metadata.mapperClass());
|
||||||
|
methodMap.putIfAbsent(adapterMethodMetadata.getMethodName(), adapterMethodMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAdapterMapMethod(ClassName source, ClassName target, ClassName mapper, boolean objectConverter) {
|
private void addAdapterMapMethod(ClassName source, ClassName target, ClassName mapper, boolean objectConverter) {
|
||||||
final AdapterMapMethodMetadata adapterMapMethodMetadata =
|
final AdapterMapMethodMetadata adapterMapMethodMetadata =
|
||||||
new AdapterMapMethodMetadata(source, target, mapper, objectConverter);
|
new AdapterMapMethodMetadata(source, target, mapper, objectConverter);
|
||||||
mapMethodMap.put(adapterMapMethodMetadata.getMethodName(), adapterMapMethodMetadata);
|
mapMethodMap.putIfAbsent(adapterMapMethodMetadata.getMethodName(), adapterMapMethodMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AutoMapperMetadata initAutoMapperMetadata(ClassName source, ClassName target) {
|
private AutoMapperMetadata initAutoMapperMetadata(ClassName source, ClassName target) {
|
||||||
|
|||||||
@ -0,0 +1,104 @@
|
|||||||
|
package io.github.linpeilie.processor;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.processing.Filer;
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.tools.FileObject;
|
||||||
|
import javax.tools.StandardLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建校对器
|
||||||
|
*
|
||||||
|
* 兼容 IDEA 部分构建的问题
|
||||||
|
*/
|
||||||
|
public class BuildCollator {
|
||||||
|
|
||||||
|
private final ProcessingEnvironment processingEnv;
|
||||||
|
|
||||||
|
private final File collatorFile;
|
||||||
|
|
||||||
|
private List<TypeElement> records;
|
||||||
|
|
||||||
|
public BuildCollator(ProcessingEnvironment processingEnv, String fileName) {
|
||||||
|
this.processingEnv = processingEnv;
|
||||||
|
Filer filer = processingEnv.getFiler();
|
||||||
|
try {
|
||||||
|
FileObject fileObject = filer.getResource(StandardLocation.CLASS_OUTPUT, "",
|
||||||
|
Constants.MAPSTRUCT_PLUS_META_INF + File.separator + fileName);
|
||||||
|
this.collatorFile = new File(fileObject.getName());
|
||||||
|
if (collatorFile.exists()) {
|
||||||
|
records = FileUtil.readUtf8Lines(collatorFile)
|
||||||
|
.stream()
|
||||||
|
.map(line -> processingEnv.getElementUtils().getTypeElement(line))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
|
records = new ArrayList<>();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTypeElements(Collection<TypeElement> typeElements) {
|
||||||
|
final Set<String> names = typeElements.stream()
|
||||||
|
.map(typeElement -> typeElement.getQualifiedName().toString())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
write(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(Collection<String> lines) {
|
||||||
|
if (CollectionUtil.isEmpty(lines)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
records = lines.stream()
|
||||||
|
.map(line -> processingEnv.getElementUtils().getTypeElement(line))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
FileUtil.mkParentDirs(collatorFile);
|
||||||
|
FileUtil.writeUtf8Lines(lines, collatorFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> loadRecords() {
|
||||||
|
return records.stream()
|
||||||
|
.map(ele -> ele.getQualifiedName().toString())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TypeElement> getRecords() {
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void consumeRecords(Consumer<TypeElement> consumer) {
|
||||||
|
final List<TypeElement> typeElements = getRecords();
|
||||||
|
if (CollectionUtil.isNotEmpty(typeElements)) {
|
||||||
|
typeElements.forEach(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendNonexistent(Collection<? extends Element> newRecords) {
|
||||||
|
final List<String> lines = loadRecords();
|
||||||
|
newRecords.forEach(ele -> {
|
||||||
|
final String classQualifiedName = ((TypeElement) ele).getQualifiedName().toString();
|
||||||
|
lines.add(classQualifiedName);
|
||||||
|
});
|
||||||
|
write(new HashSet<>(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package io.github.linpeilie.processor;
|
package io.github.linpeilie.processor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import org.mapstruct.MappingConstants;
|
import org.mapstruct.MappingConstants;
|
||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
@ -30,15 +31,22 @@ public class Constants {
|
|||||||
|
|
||||||
public static final String COMPONENT_MODEL_CONFIG_ANNOTATION = "io.github.linpeilie.annotations.ComponentModelConfig";
|
public static final String COMPONENT_MODEL_CONFIG_ANNOTATION = "io.github.linpeilie.annotations.ComponentModelConfig";
|
||||||
|
|
||||||
public static final String BASE_MAPPER_PACKAGE = "io.github.linpeilie";
|
|
||||||
|
|
||||||
public static final String BASE_MAPPER_CLASS_NAME = "BaseMapper";
|
|
||||||
|
|
||||||
public static final String BASE_MAP_MAPPER_CLASS_NAME = "BaseMapMapper";
|
|
||||||
|
|
||||||
public static final String MAPSTRUCT_MAPPER_PACKAGE = "org.mapstruct";
|
public static final String MAPSTRUCT_MAPPER_PACKAGE = "org.mapstruct";
|
||||||
|
|
||||||
public static final String MAPSTRUCT_MAPPER_CLASS_NAME = "Mapper";
|
public static final String MAPSTRUCT_MAPPER_CLASS_NAME = "Mapper";
|
||||||
|
|
||||||
|
public static final String MAPSTRUCT_PLUS_META_INF = "META-INF" + File.separator + "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";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package io.github.linpeilie.processor.generator;
|
package io.github.linpeilie.processor.generator;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.squareup.javapoet.AnnotationSpec;
|
import com.squareup.javapoet.AnnotationSpec;
|
||||||
import com.squareup.javapoet.ClassName;
|
import com.squareup.javapoet.ClassName;
|
||||||
@ -9,7 +10,7 @@ import com.squareup.javapoet.MethodSpec;
|
|||||||
import com.squareup.javapoet.ParameterSpec;
|
import com.squareup.javapoet.ParameterSpec;
|
||||||
import com.squareup.javapoet.ParameterizedTypeName;
|
import com.squareup.javapoet.ParameterizedTypeName;
|
||||||
import com.squareup.javapoet.TypeSpec;
|
import com.squareup.javapoet.TypeSpec;
|
||||||
import io.github.linpeilie.processor.AutoMapperProperties;
|
import io.github.linpeilie.annotations.Immutable;
|
||||||
import io.github.linpeilie.processor.metadata.AutoMapperMetadata;
|
import io.github.linpeilie.processor.metadata.AutoMapperMetadata;
|
||||||
import io.github.linpeilie.processor.metadata.AutoMappingMetadata;
|
import io.github.linpeilie.processor.metadata.AutoMappingMetadata;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -21,17 +22,21 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.mapstruct.ReportingPolicy;
|
|
||||||
|
|
||||||
import static io.github.linpeilie.processor.Constants.*;
|
import static io.github.linpeilie.processor.Constants.*;
|
||||||
|
|
||||||
public class AutoMapperGenerator {
|
public class AutoMapperGenerator {
|
||||||
|
|
||||||
public void write(AutoMapperMetadata metadata, Writer writer) {
|
public static final String CONVERT_METHOD_NAME = "convert";
|
||||||
|
|
||||||
|
public void write(AutoMapperMetadata metadata, final ProcessingEnvironment processingEnv, Writer writer) {
|
||||||
try {
|
try {
|
||||||
JavaFile.builder(metadata.mapperPackage(), createTypeSpec(metadata)).build().writeTo(writer);
|
JavaFile.builder(metadata.mapperPackage(), createTypeSpec(processingEnv, metadata)).build().writeTo(writer);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -39,36 +44,73 @@ public class AutoMapperGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypeSpec createTypeSpec(AutoMapperMetadata metadata) {
|
private TypeSpec createTypeSpec(ProcessingEnvironment processingEnv, AutoMapperMetadata metadata) {
|
||||||
ParameterizedTypeName converterName =
|
ParameterizedTypeName converterName =
|
||||||
ParameterizedTypeName.get(metadata.getSuperClass(), metadata.getSuperGenerics());
|
ParameterizedTypeName.get(metadata.getSuperClass(), metadata.getSuperGenerics());
|
||||||
|
|
||||||
|
final ClassName targetClassName = metadata.getTargetClassName();
|
||||||
|
|
||||||
|
|
||||||
TypeSpec.Builder builder = TypeSpec.interfaceBuilder(metadata.mapperName())
|
TypeSpec.Builder builder = TypeSpec.interfaceBuilder(metadata.mapperName())
|
||||||
.addSuperinterface(converterName)
|
.addSuperinterface(converterName)
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
.addAnnotation(buildGeneratedMapperAnnotationSpec(metadata));
|
.addAnnotation(buildGeneratedMapperAnnotationSpec(metadata));
|
||||||
if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) {
|
|
||||||
final ParameterSpec source = ParameterSpec.builder(metadata.getSourceClassName(), "source").build();
|
final ParameterSpec source = ParameterSpec.builder(metadata.getSourceClassName(), "source").build();
|
||||||
final ParameterSpec target = ParameterSpec.builder(metadata.getTargetClassName(), "target")
|
final ParameterSpec target = ParameterSpec.builder(targetClassName, "target")
|
||||||
.addAnnotation(AnnotationSpec.builder(ClassName.get("org.mapstruct", "MappingTarget")).build())
|
.addAnnotation(AnnotationSpec.builder(ClassName.get("org.mapstruct", "MappingTarget")).build())
|
||||||
.build();
|
.build();
|
||||||
|
if (metadata.getFieldMappingList() != null && !metadata.getFieldMappingList().isEmpty()) {
|
||||||
builder.addMethod(addConvertMethodSpec(Collections.singletonList(source), metadata.getFieldMappingList(),
|
builder.addMethod(addConvertMethodSpec(Collections.singletonList(source), metadata.getFieldMappingList(),
|
||||||
metadata.getTargetClassName()));
|
targetClassName));
|
||||||
builder.addMethod(addConvertMethodSpec(Arrays.asList(source, target), metadata.getFieldMappingList(),
|
|
||||||
metadata.getTargetClassName()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean targetIsImmutable = classIsImmutable(processingEnv, targetClassName);
|
||||||
|
if (targetIsImmutable) {
|
||||||
|
builder.addMethod(addEmptyConvertMethodForImmutableEntity(source, target, targetClassName));
|
||||||
|
} else {
|
||||||
|
builder.addMethod(addConvertMethodSpec(Arrays.asList(source, target), metadata.getFieldMappingList(),
|
||||||
|
targetClassName));
|
||||||
|
}
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MethodSpec addEmptyConvertMethodForImmutableEntity(ParameterSpec source,
|
||||||
|
ParameterSpec target,
|
||||||
|
ClassName targetClassName) {
|
||||||
|
return MethodSpec.methodBuilder(CONVERT_METHOD_NAME)
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(source)
|
||||||
|
.addParameter(target)
|
||||||
|
.returns(targetClassName)
|
||||||
|
.addCode("return target;")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean classIsImmutable(ProcessingEnvironment processingEnv, ClassName className) {
|
||||||
|
final TypeElement targetElement = processingEnv.getElementUtils()
|
||||||
|
.getTypeElement(className.packageName() + "." + className.simpleName());
|
||||||
|
final List<? extends AnnotationMirror> annotationMirrors = targetElement.getAnnotationMirrors();
|
||||||
|
for (AnnotationMirror annotationMirror : annotationMirrors) {
|
||||||
|
if (annotationMirror.getAnnotationType().asElement().getSimpleName().contentEquals("Immutable")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private MethodSpec addConvertMethodSpec(List<ParameterSpec> parameterSpecs,
|
private MethodSpec addConvertMethodSpec(List<ParameterSpec> parameterSpecs,
|
||||||
List<AutoMappingMetadata> autoMappingMetadataList,
|
List<AutoMappingMetadata> autoMappingMetadataList,
|
||||||
ClassName target) {
|
ClassName target) {
|
||||||
return MethodSpec.methodBuilder("convert")
|
final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(CONVERT_METHOD_NAME)
|
||||||
.addParameters(parameterSpecs)
|
.addParameters(parameterSpecs)
|
||||||
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
|
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
|
||||||
.addAnnotations(buildMappingAnnotations(autoMappingMetadataList))
|
.returns(target);
|
||||||
.returns(target)
|
if (CollectionUtil.isNotEmpty(autoMappingMetadataList)) {
|
||||||
.build();
|
methodSpecBuilder.addAnnotations(buildMappingAnnotations(autoMappingMetadataList));
|
||||||
|
}
|
||||||
|
return methodSpecBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<AnnotationSpec> buildMappingAnnotations(final List<AutoMappingMetadata> autoMappingMetadataList) {
|
private List<AnnotationSpec> buildMappingAnnotations(final List<AutoMappingMetadata> autoMappingMetadataList) {
|
||||||
|
|||||||
@ -20,12 +20,6 @@ import static javax.tools.Diagnostic.Kind.ERROR;
|
|||||||
public class MapperConfigGenerator {
|
public class MapperConfigGenerator {
|
||||||
|
|
||||||
public void write(ProcessingEnvironment processingEnv, String mapstructConfigName, String adapterClassName, List<TypeMirror> uses) {
|
public void write(ProcessingEnvironment processingEnv, String mapstructConfigName, String adapterClassName, List<TypeMirror> uses) {
|
||||||
final TypeElement typeElement =
|
|
||||||
processingEnv.getElementUtils().getTypeElement(AutoMapperProperties.getConfigPackage() + "." + adapterClassName);
|
|
||||||
if (typeElement != null) {
|
|
||||||
System.out.println("mapperConfig class existed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try (final Writer writer = processingEnv.getFiler()
|
try (final Writer writer = processingEnv.getFiler()
|
||||||
.createSourceFile(AutoMapperProperties.getConfigPackage() + "." + mapstructConfigName)
|
.createSourceFile(AutoMapperProperties.getConfigPackage() + "." + mapstructConfigName)
|
||||||
.openWriter()) {
|
.openWriter()) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package io.github.linpeilie.mapstruct;
|
|||||||
|
|
||||||
import io.github.linpeilie.Converter;
|
import io.github.linpeilie.Converter;
|
||||||
import io.github.linpeilie.ConverterFactory;
|
import io.github.linpeilie.ConverterFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
@ -12,11 +13,13 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
public class MapstructAutoConfiguration {
|
public class MapstructAutoConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
public ConverterFactory converterFactory(ApplicationContext applicationContext) {
|
public ConverterFactory converterFactory(ApplicationContext applicationContext) {
|
||||||
return new SpringConverterFactory(applicationContext);
|
return new SpringConverterFactory(applicationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
public Converter converter(ConverterFactory converterFactory) {
|
public Converter converter(ConverterFactory converterFactory) {
|
||||||
return new Converter(converterFactory);
|
return new Converter(converterFactory);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,11 +11,13 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <S, T> BaseMapper<S, T> getMapper(final Class<S> sourceType, final Class<T> targetType) {
|
public <S, T> BaseMapper<S, T> getMapper(final Class<S> sourceType, final Class<T> targetType) {
|
||||||
final String key = key(sourceType, targetType);
|
final Class<?> source = wrapperClass(sourceType);
|
||||||
|
final Class<?> target = wrapperClass(targetType);
|
||||||
|
final String key = key(source, target);
|
||||||
if (mapperMap.containsKey(key)) {
|
if (mapperMap.containsKey(key)) {
|
||||||
return mapperMap.get(key);
|
return mapperMap.get(key);
|
||||||
}
|
}
|
||||||
final BaseMapper mapper = findMapper(sourceType, targetType);
|
final BaseMapper mapper = findMapper(source, target);
|
||||||
if (mapper != null) {
|
if (mapper != null) {
|
||||||
mapperMap.put(key, mapper);
|
mapperMap.put(key, mapper);
|
||||||
return mapper;
|
return mapper;
|
||||||
@ -25,11 +27,12 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <S> BaseMapMapper<S> getMapMapper(final Class<S> sourceType) {
|
public <S> BaseMapMapper<S> getMapMapper(final Class<S> sourceType) {
|
||||||
final String key = sourceType.getName();
|
final Class<?> source = wrapperClass(sourceType);
|
||||||
|
final String key = source.getName();
|
||||||
if (mapMapperMap.containsKey(key)) {
|
if (mapMapperMap.containsKey(key)) {
|
||||||
return mapMapperMap.get(key);
|
return mapMapperMap.get(key);
|
||||||
}
|
}
|
||||||
final BaseMapMapper mapper = findMapMapper(sourceType);
|
final BaseMapMapper mapper = findMapMapper(source);
|
||||||
if (mapper != null) {
|
if (mapper != null) {
|
||||||
mapMapperMap.put(key, mapper);
|
mapMapperMap.put(key, mapper);
|
||||||
return mapper;
|
return mapper;
|
||||||
@ -37,6 +40,10 @@ public abstract class AbstractCachedConverterFactory implements ConverterFactory
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Class<?> wrapperClass(final Class<?> clazz) {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract <S, T> BaseMapper findMapper(final Class<S> source, final Class<T> target);
|
protected abstract <S, T> BaseMapper findMapper(final Class<S> source, final Class<T> target);
|
||||||
|
|
||||||
protected abstract <S> BaseMapMapper findMapMapper(final Class<?> source);
|
protected abstract <S> BaseMapMapper findMapMapper(final Class<?> source);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package io.github.linpeilie;
|
package io.github.linpeilie;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -14,7 +15,7 @@ public interface BaseMapper<S, T> {
|
|||||||
|
|
||||||
default List<T> convert(List<S> sourceList) {
|
default List<T> convert(List<S> sourceList) {
|
||||||
if (CollectionUtil.isEmpty(sourceList)) {
|
if (CollectionUtil.isEmpty(sourceList)) {
|
||||||
return Collections.emptyList();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return sourceList.stream().map(this::convert).collect(Collectors.toList());
|
return sourceList.stream().map(this::convert).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package io.github.linpeilie;
|
package io.github.linpeilie;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -49,7 +49,7 @@ public class Converter {
|
|||||||
|
|
||||||
public <S, T> List<T> convert(List<S> source, Class<T> targetType) {
|
public <S, T> List<T> convert(List<S> source, Class<T> targetType) {
|
||||||
if (source == null || source.size() == 0) {
|
if (source == null || source.size() == 0) {
|
||||||
return Collections.emptyList();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return source.stream().map(item -> convert(item, targetType)).collect(Collectors.toList());
|
return source.stream().map(item -> convert(item, targetType)).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
package io.github.linpeilie.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface Immutable {
|
||||||
|
}
|
||||||
2
pom.xml
2
pom.xml
@ -17,7 +17,7 @@
|
|||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<mapstruct-plus.version>1.3.1</mapstruct-plus.version>
|
<mapstruct-plus.version>1.3.2</mapstruct-plus.version>
|
||||||
<maven.compiler.source>8</maven.compiler.source>
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
<maven.compiler.target>8</maven.compiler.target>
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user