diff --git a/autil-incubator/incubator-annotation/pom.xml b/autil-incubator/incubator-annotation/pom.xml index d4aa8ae..f0c7a04 100644 --- a/autil-incubator/incubator-annotation/pom.xml +++ b/autil-incubator/incubator-annotation/pom.xml @@ -9,17 +9,17 @@ com.acanx.util incubator-annotation - 0.2.0.0-SNAPSHOT + 0.4.3-SNAPSHOT jar Incubator-Annotation AUtil Incubator Annotation Module https://acanx.com - 11 + 21 UTF-8 - 11 - 3.14.0 + false + 3.14.1 @@ -28,42 +28,23 @@ javapoet 1.13.0 - - - - - - - - - - - - - - - - - - - com.acanx.util autil-core ${revision} - - - - - - org.junit.jupiter junit-jupiter-api ${junit-jupiter.version} test + + + + + + @@ -81,14 +62,38 @@ ${java.version} ${java.version} - - - - com.acanx.util - incubator-annotation - 0.2.0.0-SNAPSHOT - - + UTF-8 + false + + --add-exports + jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + + + --add-opens=jdk.compiler/com.sun.tools.javac.util=com.acanx.util.incubator.annotation + + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + + + + + + + + diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ASTScanner.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ASTScanner.java deleted file mode 100644 index f44eba7..0000000 --- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ASTScanner.java +++ /dev/null @@ -1,301 +0,0 @@ -package com.acanx.util.incubator.annotation;//package com.acanx.util.object.copy; -// -//import com.sun.tools.javac.code.Symbol; -//import com.sun.tools.javac.code.Type; -//import com.sun.tools.javac.model.JavacElements; -//import com.sun.tools.javac.tree.JCTree; -//import com.sun.tools.javac.tree.TreeMaker; -//import com.sun.tools.javac.tree.TreeScanner; -//import com.sun.tools.javac.util.List; -//import com.sun.tools.javac.util.Names; -// -//import javax.lang.model.element.ElementKind; -//import java.util.Arrays; -//import java.util.HashMap; -//import java.util.HashSet; -//import java.util.Map; -//import java.util.Set; -// -///** -// * ASTScanner -// * -// * @author ACANX -// * @date 2025-06-14 -// * @since 202506 -// */ -//public class ASTScanner extends TreeScanner { -// private final TreeMaker maker; -// private final JavacElements elements; -// private final Names names; -// -// ASTScanner(TreeMaker maker, JavacElements elements, Names names) { -// this.maker = maker; -// this.elements = elements; -// this.names = names; -// } -// // JDK 8 兼容方法 -// @SuppressWarnings("unused") -// public void scan(JCTree tree, Void ignored) { -// if (tree != null) { -// tree.accept(this); -// } -// } -// -// // JDK 11 兼容的扫描方法 -// @Override -// public void visitTopLevel(JCTree.JCCompilationUnit tree) { -// super.visitTopLevel(tree); -// // 扫描顶级元素 -// for (JCTree def : tree.defs) { -// if (def instanceof JCTree.JCClassDecl) { -// visitClassDef((JCTree.JCClassDecl) def); -// } -// } -// } -// -// @Override -// public void visitClassDef(JCTree.JCClassDecl tree) { -// super.visitClassDef(tree); -// // 扫描类中的所有方法 -// for (JCTree member : tree.defs) { -// if (member instanceof JCTree.JCMethodDecl) { -// processMethod((JCTree.JCMethodDecl) member); -// } -// } -// } -// -// private void processMethod(JCTree.JCMethodDecl method) { -// // 获取方法上的所有MetaObjectCopy注解 -// List annotations = getMetaObjectCopyAnnotations(method); -// if (!annotations.isEmpty()) { -// // 合并所有注解配置 -// boolean copyNulls = annotations.stream().anyMatch(MetaObjectCopy::copyNulls); -// Set ignoreFields = new HashSet<>(); -// Map fieldMappings = new HashMap<>(); -// for (MetaObjectCopy ann : annotations) { -// ignoreFields.addAll(Arrays.asList(ann.ignoreFields())); -// for (MetaObjectCopy.FieldMapping mapping : ann.fieldMappings()) { -// fieldMappings.put(mapping.target(), mapping.source()); -// } -// } -// // 替换方法体 -// method.body = createMethodBody( -// method.params.get(0), -// method.params.get(1), -// copyNulls, -// ignoreFields, -// fieldMappings -// ); -// } -// } -// -// private List getMetaObjectCopyAnnotations(JCTree.JCMethodDecl method) { -// List annotations = List.nil(); -// for (JCTree.JCAnnotation ann : method.mods.annotations) { -// Type type = ann.type; -// if (type != null) { -// String annotationName = type.toString(); -// if (annotationName.equals(MetaObjectCopy.class.getName())) { -// // 解析注解属性 -// annotations.add(parseAnnotation(ann)); -// } else if (annotationName.equals(MetaObjectCopy.List.class.getName())) { -// // 处理多个注解 -// for (JCTree.JCExpression expr : ann.args) { -// if (expr instanceof JCTree.JCAnnotation) { -// annotations.add(parseAnnotation((JCTree.JCAnnotation) expr)); -// } -// } -// } -// } -// } -// return annotations; -// } -// -// private MetaObjectCopy parseAnnotation(JCTree.JCAnnotation ann) { -// // 简化处理 - 实际应解析所有属性 -// return new MetaObjectCopy() { -// @Override -// public boolean copyNulls() { -// return false; // 简化实现 -// } -// -// @Override -// public String[] ignoreFields() { -// return new String[0]; // 简化实现 -// } -// -// @Override -// public FieldMapping[] fieldMappings() { -// return new FieldMapping[0]; // 简化实现 -// } -// -// @Override -// public Class annotationType() { -// return MetaObjectCopy.class; -// } -// }; -// } -// -// private JCTree.JCBlock createMethodBody( -// JCTree.JCVariableDecl sourceParam, -// JCTree.JCVariableDecl targetParam, -// boolean copyNulls, -// Set ignoreFields, -// Map fieldMappings -// ) { -// List statements = com.sun.tools.javac.util.List.nil(); -// -// // 1. 空值检查 -// statements = statements.append(createNullCheck(sourceParam, targetParam)); -// // 2. 获取源和目标类型 -// Symbol.ClassSymbol sourceType = (Symbol.ClassSymbol) sourceParam.vartype.type.tsym; -// Symbol.ClassSymbol targetType = (Symbol.ClassSymbol) targetParam.vartype.type.tsym; -// // 3. 生成字段拷贝语句 -// statements = statements.appendList(createFieldCopyStatements( -// sourceParam, -// targetParam, -// sourceType, -// targetType, -// copyNulls, -// ignoreFields, -// fieldMappings -// )); -// return maker.Block(0, statements); -// } -// -// private JCTree.JCStatement createNullCheck(JCTree.JCVariableDecl source, JCTree.JCVariableDecl target) { -// // if (source == null || target == null) return; -// return maker.If( -// maker.Binary( -// JCTree.Tag.OR, -// maker.Binary( -// JCTree.Tag.EQ, -// maker.Ident(source.name), -// maker.Literal(null) -// ), -// maker.Binary( -// JCTree.Tag.EQ, -// maker.Ident(target.name), -// maker.Literal(null) -// ) -// ), -// maker.Block(0, List.of(maker.Return(null))), -// null -// ); -// } -// -// private List createFieldCopyStatements( -// JCTree.JCVariableDecl sourceParam, -// JCTree.JCVariableDecl targetParam, -// Symbol.ClassSymbol sourceType, -// Symbol.ClassSymbol targetType, -// boolean copyNulls, -// Set ignoreFields, -// Map fieldMappings) { -// List statements = com.sun.tools.javac.util.List.nil(); -// // 获取目标字段列表 -// Map targetFields = getAccessibleFields(targetType); -// for (Map.Entry entry : targetFields.entrySet()) { -// String targetFieldName = entry.getKey(); -// Symbol.VarSymbol targetField = entry.getValue(); -// // 检查是否忽略该字段 -// if (ignoreFields.contains(targetFieldName)) { -// continue; -// } -// // 获取源字段名(考虑自定义映射) -// String sourceFieldName = fieldMappings.getOrDefault(targetFieldName, targetFieldName); -// // 获取源字段 -// Symbol.VarSymbol sourceField = getField(sourceType, sourceFieldName); -// if (sourceField == null) { -// continue; // 源对象没有该字段 -// } -// // 生成字段拷贝语句 -// JCTree.JCStatement copyStmt = createSingleFieldCopy( -// sourceParam, -// targetParam, -// sourceField, -// targetField, -// copyNulls -// ); -// if (copyStmt != null) { -// statements = statements.append(copyStmt); -// } -// } -// return statements; -// } -// -// private Map getAccessibleFields(Symbol.ClassSymbol clazz) { -// Map fields = new HashMap<>(); -// // 获取所有字段(包括继承的) -// for (Symbol member : elements.getAllMembers(clazz)) { -// if (member.getKind().equals(ElementKind.FIELD)){ -// Symbol.VarSymbol field = (Symbol.VarSymbol) member; -// fields.put(field.getSimpleName().toString(), field); -// } -// } -// return fields; -// } -// -// private Symbol.VarSymbol getField(Symbol.ClassSymbol clazz, String fieldName) { -// for (Symbol member : elements.getAllMembers(clazz)) { -// if (member.getKind().equals(ElementKind.FIELD) && -// member.getSimpleName().toString().equals(fieldName)) { -// return (Symbol.VarSymbol) member; -// } -// } -// return null; -// } -// -// private JCTree.JCStatement createSingleFieldCopy( -// JCTree.JCVariableDecl sourceParam, -// JCTree.JCVariableDecl targetParam, -// Symbol.VarSymbol sourceField, -// Symbol.VarSymbol targetField, -// boolean copyNulls) { -// try { -// // 1. 构建getter调用 -// String getterName = getAccessorName(sourceField.getSimpleName().toString(), "get"); -// JCTree.JCExpression getterCall = maker.Apply( -// com.sun.tools.javac.util.List.nil(), -// maker.Select(maker.Ident(sourceParam.name), names.fromString(getterName)), -// com.sun.tools.javac.util.List.nil() -// ); -// // 2. 构建setter调用 -// String setterName = getAccessorName(targetField.getSimpleName().toString(), "set"); -// JCTree.JCExpression setterCall = maker.Apply( -// com.sun.tools.javac.util.List.nil(), -// maker.Select(maker.Ident(targetParam.name), names.fromString(setterName)), -// List.of(getterCall) -// ); -// // 3. 基本类型直接赋值 -// if (targetField.type.isPrimitive()) { -// return maker.Exec(setterCall); -// } -// // 4. 非基本类型处理 -// if (copyNulls) { -// // 允许空值 - 直接赋值 -// return maker.Exec(setterCall); -// } else { -// // 不允许空值 - 添加空值检查 -// JCTree.JCExpression notNullCheck = maker.Binary( -// JCTree.Tag.NE, -// getterCall, -// maker.Literal(null) -// ); -// return maker.If( -// notNullCheck, -// maker.Exec(setterCall), -// null -// ); -// } -// } catch (Exception e) { -// // getter/setter不存在 -// return null; -// } -// } -// -// private String getAccessorName(String fieldName, String prefix) { -// return prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); -// } -//} -// diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/App.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/App.java deleted file mode 100644 index 3586920..0000000 --- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/App.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.acanx.util.incubator.annotation; - -/** - * - * App - * - */ -public class App { - public static void main(String[] args) { - System.out.println("Hello World!"); - } -} diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/Copier.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/Copier.java new file mode 100644 index 0000000..6f5b1f1 --- /dev/null +++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/Copier.java @@ -0,0 +1,54 @@ +package com.acanx.util.incubator.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 对象拷贝注解,用于标记需要进行编译期增强的对象拷贝方法 + * 被注解的方法会在编译期被增强,自动实现从第一个参数到第二个参数的对象拷贝 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Copier { + + /** + * 拷贝策略,默认为浅拷贝 + */ + CopyStrategy strategy() default CopyStrategy.SHALLOW; + + /** + * 是否忽略空值,如果为true则源对象为null的字段不会覆盖目标对象的字段 + */ + boolean ignoreNull() default false; + + /** + * 需要排除的字段名列表 + */ + String[] exclude() default {}; + + /** + * 仅包含的字段名列表,如果设置了则只拷贝这些字段 + */ + String[] include() default {}; + + /** + * 是否使用getter/setter方法进行拷贝 + * + * @return + */ + boolean useAccessors () default true; + + /** + * 拷贝策略枚举 + */ + enum CopyStrategy { + /** 浅拷贝,直接赋值 */ + SHALLOW, + /** 深拷贝,对引用类型创建新对象 */ + DEEP + } + + +} diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/CopierProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/CopierProcessor.java new file mode 100644 index 0000000..91fc03a --- /dev/null +++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/CopierProcessor.java @@ -0,0 +1,416 @@ +package com.acanx.util.incubator.annotation; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@SupportedAnnotationTypes("com.acanx.util.incubator.annotation.Copier") +@SupportedSourceVersion(SourceVersion.RELEASE_17) +public class CopierProcessor extends AbstractProcessor { + + private Types typeUtils; + private Elements elementUtils; + private Filer filer; + + @Override + public synchronized void init(javax.annotation.processing.ProcessingEnvironment processingEnv) { + super.init(processingEnv); + typeUtils = processingEnv.getTypeUtils(); + elementUtils = processingEnv.getElementUtils(); + filer = processingEnv.getFiler(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return false; + } + + note("开始处理 @ObjectCopier 注解", null); + + // 按被注解方法所在的类进行分组 + Map> classMethodsMap = new HashMap<>(); + for (Element element : roundEnv.getElementsAnnotatedWith(Copier.class)) { + if (element.getKind() != ElementKind.METHOD) { + error("注解@ObjectCopier只能应用于方法", element, getAnnotationMirror(element)); + continue; + } + ExecutableElement method = (ExecutableElement) element; + TypeElement enclosingClass = (TypeElement) method.getEnclosingElement(); + classMethodsMap.computeIfAbsent(enclosingClass, k -> new ArrayList<>()).add(method); + + note("找到被注解的方法: " + enclosingClass.getSimpleName() + "." + method.getSimpleName(), method); + } + + // 为每个包含被注解方法的类生成一个拷贝类 + for (Map.Entry> entry : classMethodsMap.entrySet()) { + TypeElement enclosingClass = entry.getKey(); + List methods = entry.getValue(); + try { + generateCopierClass(enclosingClass, methods); + } catch (Exception e) { + error("生成拷贝类时出错: " + e.getMessage(), enclosingClass, null); + } + } + + note("注解处理完成", null); + return true; + } + + private void generateCopierClass(TypeElement enclosingClass, List methods) throws IOException { + // 验证所有方法 + List validMethods = new ArrayList<>(); + for (ExecutableElement method : methods) { + if (validateMethod(method)) { + validMethods.add(method); + } + } + + if (validMethods.isEmpty()) { + return; + } + + // 生成类名:使用被注解方法所在类的类名 + "Copier" + String enclosingClassName = enclosingClass.getSimpleName().toString(); + String copierClassName = enclosingClassName + "Copier"; + + // 获取包名 + String packageName = elementUtils.getPackageOf(enclosingClass).getQualifiedName().toString(); + + // 创建源文件 + JavaFileObject builderFile = filer.createSourceFile( + packageName + "." + copierClassName, + enclosingClass + ); + + note("生成拷贝类: " + packageName + "." + copierClassName + ",包含 " + validMethods.size() + " 个方法", enclosingClass); + + try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { + writeCopierClass(out, packageName, copierClassName, validMethods, enclosingClass); + } + } + + private void writeCopierClass(PrintWriter out, String packageName, String className, + List methods, TypeElement enclosingClass) { + + String enclosingClassQualifiedName = enclosingClass.getQualifiedName().toString(); + + // 包声明 + out.println("package " + packageName + ";"); + out.println(); + + // 收集所有需要导入的类型 + Set imports = collectImports(methods); + for (String importLine : imports) { + out.println("import " + importLine + ";"); + } + out.println(); + + // 类注释 + out.println("/**"); + out.println(" * 自动生成的拷贝类"); + out.println(" * 包含 " + methods.size() + " 个拷贝方法"); + out.println(" * 由 {@link " + enclosingClassQualifiedName + "} 中的方法生成"); + out.println(" */"); + + // 类声明 + out.println("public class " + className + " {"); + out.println(); + + // 为每个方法生成拷贝方法 + for (ExecutableElement method : methods) { + writeCopierMethod(out, method, enclosingClass); + out.println(); + } + + out.println("}"); + } + + private Set collectImports(List methods) { + Set imports = new java.util.HashSet<>(); + for (ExecutableElement method : methods) { + List parameters = method.getParameters(); + if (parameters.size() >= 2) { + TypeMirror sourceType = parameters.get(0).asType(); + TypeMirror targetType = parameters.get(1).asType(); + + if (sourceType instanceof DeclaredType) { + TypeElement sourceElement = (TypeElement) ((DeclaredType) sourceType).asElement(); + imports.add(sourceElement.getQualifiedName().toString()); + } + + if (targetType instanceof DeclaredType) { + TypeElement targetElement = (TypeElement) ((DeclaredType) targetType).asElement(); + imports.add(targetElement.getQualifiedName().toString()); + } + } + } + return imports; + } + + private void writeCopierMethod(PrintWriter out, ExecutableElement methodElement, TypeElement enclosingClass) { + VariableElement sourceParam = methodElement.getParameters().get(0); + VariableElement targetParam = methodElement.getParameters().get(1); + TypeMirror sourceType = sourceParam.asType(); + TypeMirror targetType = targetParam.asType(); + + if (!(sourceType instanceof DeclaredType) || !(targetType instanceof DeclaredType)) { + return; + } + + TypeElement sourceElement = (TypeElement) ((DeclaredType) sourceType).asElement(); + TypeElement targetElement = (TypeElement) ((DeclaredType) targetType).asElement(); + + String sourceTypeName = sourceElement.getSimpleName().toString(); + String targetTypeName = targetElement.getSimpleName().toString(); + String enclosingClassQualifiedName = enclosingClass.getQualifiedName().toString(); + String methodName = methodElement.getSimpleName().toString(); + + // 方法注释 + out.println(" /**"); + out.println(" * 执行属性拷贝"); + out.println(" * 将 " + sourceTypeName + " 对象的属性拷贝到 " + targetTypeName + " 对象"); + out.println(" * 由 {@link " + enclosingClassQualifiedName + "#" + methodName + "(" + sourceTypeName + ", " + targetTypeName + ")} 方法生成"); + out.println(" * @param source 源对象"); + out.println(" * @param target 目标对象"); + out.println(" */"); + + // 方法声明 + out.println(" public static void " + methodName + "(" + sourceTypeName + " source, " + targetTypeName + " target) {"); + out.println(" if (source == null || target == null) {"); + out.println(" return;"); + out.println(" }"); + out.println(); + + // 生成属性拷贝代码 + List mappings = findPropertyMappings(sourceElement, targetElement); + if (mappings.isEmpty()) { + out.println(" // 未找到可拷贝的属性"); + } else { + out.println(" // 属性拷贝"); + for (PropertyMapping mapping : mappings) { + out.println(" target." + mapping.setterCall.replace("(value)", "(" + "source." + mapping.getterCall + ")" + ";")); + } + } + out.println(" }"); + } + + private List findPropertyMappings(TypeElement sourceElement, TypeElement targetElement) { + List mappings = new ArrayList<>(); + List sourceGetters = findGetterMethods(sourceElement); + List targetSetters = findSetterMethods(targetElement); + + for (GetterMethod getter : sourceGetters) { + SetterMethod setter = findMatchingSetter(targetSetters, getter.propertyName, getter.returnType); + if (setter != null) { + mappings.add(new PropertyMapping(getter.propertyName, getter.methodCall, setter.methodCall)); + } + } + + return mappings; + } + + private String extractPropertyName(String methodName) { + if (methodName.startsWith("get") || methodName.startsWith("set")) { + String baseName = methodName.substring(3); + if (!baseName.isEmpty()) { + return Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1); + } + } else if (methodName.startsWith("is")) { + String baseName = methodName.substring(2); + if (!baseName.isEmpty()) { + return Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1); + } + } + return methodName; + } + + private List findGetterMethods(TypeElement element) { + List getters = new ArrayList<>(); + TypeMirror currentType = element.asType(); + + while (currentType.getKind() == TypeKind.DECLARED) { + DeclaredType declaredType = (DeclaredType) currentType; + TypeElement currentElement = (TypeElement) declaredType.asElement(); + + for (Element enclosed : currentElement.getEnclosedElements()) { + if (enclosed.getKind() == ElementKind.METHOD && enclosed instanceof ExecutableElement) { + ExecutableElement method = (ExecutableElement) enclosed; + String methodName = method.getSimpleName().toString(); + + if (isGetterMethod(method, methodName)) { + String propertyName = extractPropertyName(methodName); + String methodCall = methodName + "()"; + getters.add(new GetterMethod(propertyName, methodCall, method.getReturnType())); + } + } + } + + currentType = currentElement.getSuperclass(); + if (currentType.getKind() == TypeKind.NONE) { + break; + } + } + return getters; + } + + private boolean isGetterMethod(ExecutableElement method, String methodName) { + if (method.getModifiers().contains(Modifier.STATIC)) return false; + if (!method.getParameters().isEmpty()) return false; + + if (methodName.startsWith("get") && methodName.length() > 3) return true; + return methodName.startsWith("is") && methodName.length() > 2 && + method.getReturnType().getKind() == TypeKind.BOOLEAN; + } + + private List findSetterMethods(TypeElement element) { + List setters = new ArrayList<>(); + TypeMirror currentType = element.asType(); + + while (currentType.getKind() == TypeKind.DECLARED) { + DeclaredType declaredType = (DeclaredType) currentType; + TypeElement currentElement = (TypeElement) declaredType.asElement(); + + for (Element enclosed : currentElement.getEnclosedElements()) { + if (enclosed.getKind() == ElementKind.METHOD && enclosed instanceof ExecutableElement) { + ExecutableElement method = (ExecutableElement) enclosed; + String methodName = method.getSimpleName().toString(); + + if (isSetterMethod(method, methodName)) { + String propertyName = extractPropertyName(methodName); + String methodCall = methodName + "(value)"; + setters.add(new SetterMethod(propertyName, methodCall, method.getParameters().get(0).asType())); + } + } + } + + currentType = currentElement.getSuperclass(); + if (currentType.getKind() == TypeKind.NONE) { + break; + } + } + return setters; + } + + private boolean isSetterMethod(ExecutableElement method, String methodName) { + if (method.getModifiers().contains(Modifier.STATIC)) return false; + return methodName.startsWith("set") && methodName.length() > 3 && + method.getParameters().size() == 1; + } + + private SetterMethod findMatchingSetter(List setters, String propertyName, TypeMirror getterType) { + for (SetterMethod setter : setters) { + if (setter.propertyName.equals(propertyName) && isTypeCompatible(getterType, setter.paramType)) { + return setter; + } + } + return null; + } + + private boolean isTypeCompatible(TypeMirror sourceType, TypeMirror targetType) { + return typeUtils.isAssignable(sourceType, targetType) || + typeUtils.isAssignable(targetType, sourceType); + } + + private boolean validateMethod(ExecutableElement method) { + if (method.getParameters().size() != 2) { + error("@ObjectCopier注解的方法必须恰好有2个参数", method, getAnnotationMirror(method)); + return false; + } + if (!method.getReturnType().getKind().equals(TypeKind.VOID)) { + error("@ObjectCopier注解的方法必须返回void", method, getAnnotationMirror(method)); + return false; + } + return true; + } + + private String getSimpleTypeName(TypeMirror typeMirror) { + if (typeMirror instanceof DeclaredType) { + TypeElement typeElement = (TypeElement) ((DeclaredType) typeMirror).asElement(); + return typeElement.getSimpleName().toString(); + } + return typeMirror.toString(); + } + + private void error(String msg, Element element, AnnotationMirror annotationMirror) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, element, annotationMirror); + } + + private void warn(String msg, Element element) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg, element); + } + + private void note(String msg, Element element) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg, element); + } + + private AnnotationMirror getAnnotationMirror(Element element) { + for (AnnotationMirror mirror : element.getAnnotationMirrors()) { + if (mirror.getAnnotationType().toString().equals(Copier.class.getName())) { + return mirror; + } + } + return null; + } + + // 内部类保持不变... + private static class PropertyMapping { + final String propertyName; + final String getterCall; + final String setterCall; + + PropertyMapping(String propertyName, String getterCall, String setterCall) { + this.propertyName = propertyName; + this.getterCall = getterCall; + this.setterCall = setterCall; + } + } + + private static class GetterMethod { + final String propertyName; + final String methodCall; + final TypeMirror returnType; + + GetterMethod(String propertyName, String methodCall, TypeMirror returnType) { + this.propertyName = propertyName; + this.methodCall = methodCall; + this.returnType = returnType; + } + } + + private static class SetterMethod { + final String propertyName; + final String methodCall; + final TypeMirror paramType; + + SetterMethod(String propertyName, String methodCall, TypeMirror paramType) { + this.propertyName = propertyName; + this.methodCall = methodCall; + this.paramType = paramType; + } + } +} \ No newline at end of file diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyPlugin.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyPlugin.java deleted file mode 100644 index 7b6d097..0000000 --- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyPlugin.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.acanx.util.incubator.annotation;//package com.acanx.util.object.copy; -// -//import com.sun.source.util.JavacTask; -//import com.sun.source.util.Plugin; -//import com.sun.source.util.TaskEvent; -//import com.sun.source.util.TaskListener; -// -//import javax.annotation.processing.SupportedAnnotationTypes; -//import javax.annotation.processing.SupportedSourceVersion; -//import javax.lang.model.SourceVersion; -//import java.io.InputStream; -//import java.nio.charset.StandardCharsets; -// -//import com.sun.tools.javac.api.BasicJavacTask; -//import com.sun.tools.javac.model.JavacElements; -//import com.sun.tools.javac.tree.JCTree; -//import com.sun.tools.javac.tree.TreeMaker; -//import com.sun.tools.javac.util.Context; -//import com.sun.tools.javac.util.Names; -// -// -///** -// * MetaObjectCopyPlugin -// * -// * @author ACANX -// * @since 202506 -// */ -// -//@SupportedAnnotationTypes("MetaObjectCopy") -//@SupportedSourceVersion(SourceVersion.RELEASE_11) -//public class MetaObjectCopyPlugin implements Plugin { -// -// static { -// System.out.println("=== 插件初始化开始 ==="); -// String serviceFile = "META-INF/services/com.sun.source.util.Plugin"; -// try (InputStream is = MetaObjectCopyPlugin.class.getClassLoader() -// .getResourceAsStream(serviceFile)) { -// if (is == null) { -// System.err.println("❌ 服务文件未找到: " + serviceFile); -// } else { -// String content = new String(is.readAllBytes(), StandardCharsets.UTF_8); -// System.out.println("✅ 服务文件内容: " + content); -// -// if (!content.contains("com.acanx.util.object.copy.MetaObjectCopyPlugin")) { -// System.err.println("❌ 服务文件不包含本插件类名"); -// } -// } -// } catch (Exception e) { -// System.err.println("❌ 读取服务文件失败: " + e.getMessage()); -// } -// System.out.println("=== 插件初始化结束 ==="); -// } -// -// -// -// @Override -// public String getName() { -// return "MetaObjectCopy"; -// } -// -// @Override -// public void init(JavacTask task, String... args) { -// // JDK 版本检测 -// String javaVersion = System.getProperty("java.version"); -// Context context = ((BasicJavacTask) task).getContext(); -// JavacElements elements = JavacElements.instance(context); -// TreeMaker maker = TreeMaker.instance(context); -// Names names = Names.instance(context); -// -// task.addTaskListener(new TaskListener() { -// @Override -// public void started(TaskEvent e) {} -// -// @Override -// public void finished(TaskEvent e) { -// if (e.getKind() == TaskEvent.Kind.ANALYZE) { -// // JDK 11 兼容的扫描方式 -// JCTree.JCCompilationUnit compUnit = (JCTree.JCCompilationUnit) e.getCompilationUnit(); -// -// if (javaVersion.startsWith("1.8")) { -// // JDK 8 兼容方式 (使用已添加的 scan(JCTree, Void) 方法) -//// new ASTScanner(maker, elements, names).scan(compUnit, null); -// } else { -// // JDK 11+ 标准方式 -//// new ASTScanner(maker, elements, names).scan(compUnit); -// } -// } -// } -// }); -// } -// -// -//} diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyProcessor.java deleted file mode 100644 index 3be80b1..0000000 --- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyProcessor.java +++ /dev/null @@ -1,334 +0,0 @@ -//package com.acanx.util.incubator.annotation; -// -// -//import com.acanx.annotation.ObjectCopy; -//import com.google.auto.service.AutoService; -//import org.objectweb.asm.ClassReader; -//import org.objectweb.asm.ClassVisitor; -//import org.objectweb.asm.ClassWriter; -//import org.objectweb.asm.Label; -//import org.objectweb.asm.MethodVisitor; -//import org.objectweb.asm.Opcodes; -//import org.objectweb.asm.Type; -// -//import javax.annotation.processing.AbstractProcessor; -//import javax.annotation.processing.Filer; -//import javax.annotation.processing.Processor; -//import javax.annotation.processing.RoundEnvironment; -//import javax.annotation.processing.SupportedAnnotationTypes; -//import javax.annotation.processing.SupportedSourceVersion; -//import javax.lang.model.SourceVersion; -//import javax.lang.model.element.Element; -//import javax.lang.model.element.ElementKind; -//import javax.lang.model.element.ExecutableElement; -//import javax.lang.model.element.TypeElement; -//import javax.lang.model.type.ArrayType; -//import javax.lang.model.type.DeclaredType; -//import javax.lang.model.type.TypeMirror; -//import javax.tools.Diagnostic; -//import javax.tools.FileObject; -//import javax.tools.StandardLocation; -//import java.io.IOException; -//import java.io.InputStream; -//import java.io.PrintWriter; -//import java.io.StringWriter; -//import java.net.URL; -//import java.nio.file.Files; -//import java.nio.file.Path; -//import java.nio.file.Paths; -//import java.nio.file.StandardOpenOption; -//import java.util.Arrays; -//import java.util.HashMap; -//import java.util.Map; -//import java.util.Set; -// -///** -// * ObjectValueCopyProcessor -// * -// * @author ACANX -// * @since 202506 -// */ -//@SupportedAnnotationTypes("com.acanx.annotation.ObjectCopy") -//@SupportedSourceVersion(SourceVersion.RELEASE_11) -//@AutoService(Processor.class) -//public class MetaObjectCopyProcessor extends AbstractProcessor { -// -// @Override -// public boolean process(Set annotations, RoundEnvironment roundEnv) { -// for (Element element : roundEnv.getElementsAnnotatedWith(ObjectCopy.class)) { -// if (element.getKind() == ElementKind.METHOD) { -// ExecutableElement method = (ExecutableElement) element; -// processMethod(method); -// } -// } -// return true; -// } -// -// private void processMethod(ExecutableElement method) { -// try { -// // 获取类名和方法信息 -// TypeElement classElement = (TypeElement) method.getEnclosingElement(); -// String className = classElement.getQualifiedName().toString(); -// String methodName = method.getSimpleName().toString(); -// -// // 正确获取方法参数类型 -// Type originAsmType = getAsmType(method.getParameters().get(0).asType()); -// Type targetAsmType = getAsmType(method.getParameters().get(1).asType()); -// -// // 正确获取方法描述符 -// String methodDesc = Type.getMethodDescriptor(Type.VOID_TYPE, originAsmType, targetAsmType); -// -// // 获取注解配置 -// ObjectCopy annotation = method.getAnnotation(ObjectCopy.class); -// boolean copyNulls = annotation.copyNulls(); -// String[] ignoreFields = annotation.ignoreFields(); -// ObjectCopy.FieldMapping[] fieldMappings = annotation.fieldMappings(); -// -// // 读取原始类字节码 -// String binaryName = processingEnv.getElementUtils().getBinaryName(classElement).toString(); -// String relativePath = binaryName.replace('.', '/')+ ".class"; -// processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "RelativePath:"+relativePath); -// processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Class:" + getClass().getName()); -// -// -// // 4. 使用 Filer 获取资源 -// Filer filer = processingEnv.getFiler(); -// FileObject fileObject = filer.getResource( -// StandardLocation.CLASS_OUTPUT, "", relativePath); -// -// processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Path:" + fileObject.toUri().toURL()); -// InputStream input = fileObject.openInputStream(); -// if (input == null) { -// processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Class file not found: " + binaryName); -// return; -// } else { -// -// } -// ClassReader cr = new ClassReader(input); -// ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS); -// -// // 创建ClassVisitor修改方法 -// cr.accept(new ClassVisitor(Opcodes.ASM7, cw) { -// @Override -// public MethodVisitor visitMethod(int access, String name, String desc, -// String signature, String[] exceptions) { -// if (name.equals(methodName) && desc.equals(methodDesc)) { -// return new MethodVisitor(Opcodes.ASM7, -// super.visitMethod(access, name, desc, signature, exceptions)) { -// -// @Override -// public void visitCode() { -// // 完全替换方法体 -// generateNewMethodBody(this, method, annotation); -// super.visitMaxs(0, 0); // 自动计算max stack/locals -// } -// -// @Override -// public void visitEnd() { -// // 不调用super,避免原始方法体被保留 -// } -// }; -// } -// return super.visitMethod(access, name, desc, signature, exceptions); -// } -// }, ClassReader.EXPAND_FRAMES); -// -// // 将修改后的字节码写回文件系统 -// writeModifiedClass(className, cw.toByteArray()); -// -// } catch (Exception e) { -// StringWriter sw = new StringWriter(); -// e.printStackTrace(new PrintWriter(sw)); -// processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, -// "Failed to process @MetaObjectCopy: " + e.getMessage() + "\n" + sw.toString()); -// } -// } -// -// /** -// * 根据完整类名获取 .class 文件路径 -// * @param className 完整类名(如 com.example.MyClass) -// * @return .class 文件路径,如果找不到返回 null -// */ -// public static String findClassFilePath(String className) { -// try { -// // 将类名转换为资源路径 -// String resourcePath = className.replace('.', '/') + ".class"; -// // 获取系统类加载器 -// ClassLoader classLoader = ClassLoader.getSystemClassLoader(); -// // 获取资源 URL -// URL url = classLoader.getResource(resourcePath); -// if (url != null) { -// // 处理特殊字符和编码 -// String decodedPath = java.net.URLDecoder.decode(url.getPath(), "UTF-8"); -// // 处理 JAR 文件路径 -// if (decodedPath.contains("!")) { -// return decodedPath.substring(0, decodedPath.indexOf("!")); -// } -// return decodedPath; -// } -// return null; -// } catch (Exception e) { -// e.printStackTrace(); -// return null; -// } -// } -// -// private void generateNewMethodBody(MethodVisitor mv, ExecutableElement method, ObjectCopy annotation) { -// // 获取方法参数类型 -// TypeMirror originType = method.getParameters().get(0).asType(); -// TypeMirror targetType = method.getParameters().get(1).asType(); -// -// boolean copyNulls = annotation.copyNulls(); -// String[] ignoreFields = annotation.ignoreFields(); -// ObjectCopy.FieldMapping[] fieldMappings = annotation.fieldMappings(); -// -// // === 开始生成新方法体 === -// Label startLabel = new Label(); -// Label endLabel = new Label(); -// Label returnLabel = new Label(); -// -// mv.visitLabel(startLabel); -// -// // 空值检查:if (origin == null || target == null) return; -// mv.visitVarInsn(Opcodes.ALOAD, 1); // 加载origin -// mv.visitJumpInsn(Opcodes.IFNULL, returnLabel); -// mv.visitVarInsn(Opcodes.ALOAD, 2); // 加载target -// mv.visitJumpInsn(Opcodes.IFNULL, returnLabel); -// -// // 创建字段映射表 -// Map fieldMap = new HashMap<>(); -// for (ObjectCopy.FieldMapping mapping : fieldMappings) { -// fieldMap.put(mapping.target(), mapping.source()); -// } -// -// // 获取目标类字段 -// TypeElement targetElement = (TypeElement) ((DeclaredType) targetType).asElement(); -// for (Element field : targetElement.getEnclosedElements()) { -// if (field.getKind() != ElementKind.FIELD) continue; -// -// String targetField = field.getSimpleName().toString(); -// -// // 检查忽略字段 -// if (Arrays.asList(ignoreFields).contains(targetField)) continue; -// -// // 获取源字段名 -// String sourceField = fieldMap.getOrDefault(targetField, targetField); -// -// // 生成字段拷贝逻辑 -// generateFieldCopy(mv, originType, targetType, sourceField, targetField, -// field.asType(), copyNulls); -// } -// -// mv.visitJumpInsn(Opcodes.GOTO, returnLabel); -// mv.visitLabel(endLabel); -// -// // 异常处理块(空实现) -// mv.visitLabel(returnLabel); -// mv.visitInsn(Opcodes.RETURN); -// -// // 局部变量表(调试信息) -// mv.visitLocalVariable("origin", Type.getDescriptor(Object.class), null, startLabel, endLabel, 1); -// mv.visitLocalVariable("target", Type.getDescriptor(Object.class), null, startLabel, endLabel, 2); -// mv.visitMaxs(3, 3); // 自动计算 -// } -// -// private void generateFieldCopy(MethodVisitor mv, TypeMirror originType, TypeMirror targetType, -// String sourceField, String targetField, TypeMirror fieldType, -// boolean copyNulls) { -// String getter = "get" + capitalize(sourceField); -// String setter = "set" + capitalize(targetField); -// -// // 使用新的类型转换方法 -// Type asmFieldType = getAsmType(fieldType); -// String fieldDesc = asmFieldType.getDescriptor(); -// -// // 获取内部类名 -// String originInternal = getAsmType(originType).getInternalName(); -// String targetInternal = getAsmType(targetType).getInternalName(); -// -// -// Label afterSet = new Label(); -// -// // 加载target对象 -// mv.visitVarInsn(Opcodes.ALOAD, 2); -// -// // 加载origin对象 -// mv.visitVarInsn(Opcodes.ALOAD, 1); -// -// // 调用origin.getter() -// mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, originInternal, getter, "()" + fieldDesc, false); -// -// // 空值检查 -// if (!copyNulls) { -// mv.visitInsn(Opcodes.DUP); // 复制返回值 -// mv.visitJumpInsn(Opcodes.IFNULL, afterSet); // 如果为null则跳过setter -// } -// -// // 调用target.setter() -// mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, targetInternal, setter, "(" + fieldDesc + ")V", false); -// -// if (!copyNulls) { -// mv.visitLabel(afterSet); -// mv.visitInsn(Opcodes.POP); // 弹出多余的null值 -// } -// } -// -// // 工具方法保持不变 -// private String capitalize(String s) { -// return s.substring(0, 1).toUpperCase() + s.substring(1); -// } -// -// private String getInternalName(TypeMirror type) { -// return getAsmType(type).getInternalName(); -// } -// -// private String getTypeDescriptor(TypeMirror type) { -// switch (type.getKind()) { -// case BOOLEAN: return "Z"; -// case BYTE: return "B"; -// case CHAR: return "C"; -// case SHORT: return "S"; -// case INT: return "I"; -// case LONG: return "J"; -// case FLOAT: return "F"; -// case DOUBLE: return "D"; -// case VOID: return "V"; -// default: return "L" + getInternalName(type) + ";"; -// } -// } -// -// private void writeModifiedClass(String className, byte[] bytecode) throws IOException { -// String outputPath = processingEnv.getOptions().get("outputDirectory"); -// if (outputPath == null) { -// outputPath = "out/production/classes"; // 默认输出目录 -// } -// -// Path classFile = Paths.get(outputPath, className + ".class"); -// Files.createDirectories(classFile.getParent()); -// Files.write(classFile, bytecode, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); -// } -// -// -// private Type getAsmType(TypeMirror typeMirror) { -// switch (typeMirror.getKind()) { -// case BOOLEAN: return Type.BOOLEAN_TYPE; -// case BYTE: return Type.BYTE_TYPE; -// case CHAR: return Type.CHAR_TYPE; -// case SHORT: return Type.SHORT_TYPE; -// case INT: return Type.INT_TYPE; -// case LONG: return Type.LONG_TYPE; -// case FLOAT: return Type.FLOAT_TYPE; -// case DOUBLE: return Type.DOUBLE_TYPE; -// case VOID: return Type.VOID_TYPE; -// case ARRAY: -// ArrayType arrayType = (ArrayType) typeMirror; -// return Type.getType("[" + getAsmType(arrayType.getComponentType()).getDescriptor()); -// case DECLARED: -// DeclaredType declaredType = (DeclaredType) typeMirror; -// TypeElement typeElement = (TypeElement) declaredType.asElement(); -// return Type.getObjectType(typeElement.getQualifiedName().toString().replace('.', '/')); -// default: -// return Type.getType(Object.class); -// } -// } -//} diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java index 2b8647e..f844dbb 100644 --- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java +++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java @@ -1,448 +1,448 @@ -package com.acanx.util.incubator.annotation; - -import com.acanx.annotation.ObjectCopy; -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.SupportedAnnotationTypes; -import javax.annotation.processing.SupportedOptions; -import javax.annotation.processing.SupportedSourceVersion; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.ArrayType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import javax.tools.Diagnostic; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import javax.lang.model.util.Types; - -import com.squareup.javapoet.CodeBlock; -import com.squareup.javapoet.JavaFile; -import com.squareup.javapoet.MethodSpec; -import com.squareup.javapoet.TypeName; -import com.squareup.javapoet.TypeSpec; - -/** - * ObjectCopyProcessor - * - * @author ACANX - * @since 202506 - */ -@SupportedAnnotationTypes("com.acanx.annotation.ObjectCopy") -@SupportedSourceVersion(SourceVersion.RELEASE_11) -@SupportedOptions({"incremental"}) // 支持增量编译 -public class ObjectCopyProcessor extends AbstractProcessor { - - public static final String SUFFIX = "Copier"; - private Messager messager; - private Filer filer; - private Elements elementUtils; - private Types typeUtils; - - // 全局跟踪已生成的文件 - private static final Set generatedFiles = new HashSet<>(); - - // 收集使用说明 - private static final Map> usageInstructions = new HashMap<>(); - - // 基本类型与包装类型映射 - private static final Map primitiveToWrapper = Map.of( - "int", "java.lang.Integer", - "long", "java.lang.Long", - "double", "java.lang.Double", - "float", "java.lang.Float", - "boolean", "java.lang.Boolean", - "char", "java.lang.Character", - "byte", "java.lang.Byte", - "short", "java.lang.Short" - ); - - // 兼容类型映射 - private static final Map> compatibleTypes = Map.of( - "java.lang.String", Set.of("java.lang.CharSequence"), - "java.lang.CharSequence", Set.of("java.lang.String"), - "java.util.Date", Set.of("java.time.Instant", "java.time.LocalDateTime", "java.time.ZonedDateTime"), - "java.time.Instant", Set.of("java.util.Date", "java.time.LocalDateTime", "java.time.ZonedDateTime"), - "java.time.LocalDateTime", Set.of("java.util.Date", "java.time.Instant", "java.time.ZonedDateTime"), - "java.time.ZonedDateTime", Set.of("java.util.Date", "java.time.Instant", "java.time.LocalDateTime") - ); - - @Override - public synchronized void init(ProcessingEnvironment processingEnv) { - super.init(processingEnv); - messager = processingEnv.getMessager(); - filer = processingEnv.getFiler(); - elementUtils = processingEnv.getElementUtils(); - typeUtils = processingEnv.getTypeUtils(); - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - try { - if (roundEnv.processingOver()) { - printUsageInstructions(); - return false; - } - - Set elements = roundEnv.getElementsAnnotatedWith(ObjectCopy.class); - List methods = elements.stream() - .filter(e -> e.getKind() == ElementKind.METHOD) - .map(e -> (ExecutableElement) e) - .collect(Collectors.toList()); - - if (methods.isEmpty()) return false; - - Map> methodsByClass = methods.stream() - .collect(Collectors.groupingBy( - method -> (TypeElement) method.getEnclosingElement() - )); - - for (Map.Entry> entry : methodsByClass.entrySet()) { - TypeElement enclosingClass = entry.getKey(); - List classMethods = entry.getValue(); - String helperClassName = enclosingClass.getSimpleName().toString() + SUFFIX; - - createCopierClass(enclosingClass, classMethods, helperClassName); - collectUsageInstructions(enclosingClass, classMethods, helperClassName); - } - - return true; - } catch (Exception e) { - messager.printMessage(Diagnostic.Kind.ERROR, "处理器异常: " + e.getMessage()); - return false; - } - } - - private void createCopierClass(TypeElement enclosingClass, - List methods, - String helperClassName) { - try { - String packageName = elementUtils.getPackageOf(enclosingClass).toString(); - String fullClassName = packageName + "." + helperClassName; - - if (generatedFiles.contains(fullClassName)) return; - - TypeSpec.Builder helperClassBuilder = TypeSpec.classBuilder(helperClassName) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addJavadoc("Incubator-Annotation生成的对象拷贝Copier类\n\n") - .addJavadoc("支持功能:\n") - .addJavadoc("- 同名同类型字段自动复制\n") - .addJavadoc("- 基本类型与包装类型兼容\n") - .addJavadoc("- 常用类型自动转换\n") - .addJavadoc("- 字段映射配置\n\n") - .addJavadoc("方法列表:\n"); - - for (ExecutableElement method : methods) { - String methodName = method.getSimpleName().toString(); - String helperMethodName = methodName; - ObjectCopy annotation = method.getAnnotation(ObjectCopy.class); - - helperClassBuilder.addJavadoc("- $L()\n", helperMethodName); - - MethodSpec helperMethod = generateCopyMethod(method, helperMethodName, annotation); - if (helperMethod != null) { - helperClassBuilder.addMethod(helperMethod); - } - } - - JavaFile javaFile = JavaFile.builder(packageName, helperClassBuilder.build()) - .indent(" ") - .build(); - - javaFile.writeTo(filer); - generatedFiles.add(fullClassName); - - } catch (Exception e) { - messager.printMessage(Diagnostic.Kind.ERROR, "创建Copier类失败: " + e.getMessage(), enclosingClass); - } - } - - private MethodSpec generateCopyMethod(ExecutableElement method, - String helperMethodName, - ObjectCopy annotation) { - List parameters = method.getParameters(); - - if (parameters.size() != 2) { - messager.printMessage(Diagnostic.Kind.ERROR, "@ObjectCopy 方法必须有两个参数", method); - return null; - } - - VariableElement sourceParam = parameters.get(0); - VariableElement targetParam = parameters.get(1); - TypeElement sourceType = (TypeElement) typeUtils.asElement(sourceParam.asType()); - TypeElement targetType = (TypeElement) typeUtils.asElement(targetParam.asType()); - - if (sourceType == null || targetType == null) { - messager.printMessage(Diagnostic.Kind.ERROR, "无法解析源或目标类型", method); - return null; - } - - // 添加方法Javadoc - CodeBlock.Builder javadocBuilder = CodeBlock.builder(); - javadocBuilder.add("Incubator-Annotation生成的拷贝方法\n用于替换 {@link $T#$L($T, $T)}\n\n", - method.getEnclosingElement(), - method.getSimpleName().toString(), - sourceParam.asType(), - targetParam.asType()); - - javadocBuilder.add("配置信息:\n"); - javadocBuilder.add("- copyNulls: $L\n", annotation.copyNulls()); - javadocBuilder.add("- 忽略字段: $L\n", Arrays.toString(annotation.ignoreFields())); - - if (annotation.fieldMappings().length > 0) { - javadocBuilder.add("- 字段映射:\n"); - for (ObjectCopy.FieldMapping mapping : annotation.fieldMappings()) { - javadocBuilder.add(" - $L → $L\n", mapping.s(), mapping.t()); - } - } - - // 添加参数文档 - javadocBuilder.add("\n@param source 源对象,包含待拷贝的数据\n"); - javadocBuilder.add("@param target 目标对象,接收拷贝后的数据\n"); - - MethodSpec.Builder builder = MethodSpec.methodBuilder(helperMethodName) - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(void.class) - .addJavadoc(javadocBuilder.build()) - .addParameter(TypeName.get(sourceParam.asType()), "source") - .addParameter(TypeName.get(targetParam.asType()), "target"); - - addFieldCopyStatements(builder, annotation, sourceType, targetType); - return builder.build(); - } - - private void addFieldCopyStatements(MethodSpec.Builder builder, - ObjectCopy config, - TypeElement sourceTypeElement, - TypeElement targetTypeElement) { - - boolean copyNulls = config.copyNulls(); - Set ignoredFields = new HashSet<>(Arrays.asList(config.ignoreFields())); - - // 1. 准备字段映射 - Map fieldMappings = new HashMap<>(); - for (ObjectCopy.FieldMapping mapping : config.fieldMappings()) { - fieldMappings.put(mapping.s(), mapping.t()); - } - - // 2. 收集源类字段 - Map sourceFields = new HashMap<>(); - for (Element element : sourceTypeElement.getEnclosedElements()) { - if (element.getKind() == ElementKind.FIELD) { - VariableElement field = (VariableElement) element; - sourceFields.put(field.getSimpleName().toString(), field); - } - } - - // 3. 收集目标类字段 - Map targetFields = new HashMap<>(); - for (Element element : targetTypeElement.getEnclosedElements()) { - if (element.getKind() == ElementKind.FIELD) { - VariableElement field = (VariableElement) element; - targetFields.put(field.getSimpleName().toString(), field); - } - } - - // 4. 使用代码块构建拷贝逻辑 - CodeBlock.Builder codeBlock = CodeBlock.builder(); - codeBlock.add("if (null != source && null != target) {\n"); - - // 5. 处理自动映射的同名字段 - for (String sourceFieldName : sourceFields.keySet()) { - if (ignoredFields.contains(sourceFieldName)) continue; - if (fieldMappings.containsKey(sourceFieldName)) continue; - - VariableElement sourceField = sourceFields.get(sourceFieldName); - VariableElement targetField = targetFields.get(sourceFieldName); - - if (targetField != null) { - if (isTypeCompatible(sourceField.asType(), targetField.asType())) { - String sourceGetter = getAccessorName(sourceField, true); - String targetSetter = getAccessorName(targetField, false); - - if (copyNulls) { - codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter); - } else { - codeBlock.add(" if (null != source.$L()) {\n", sourceGetter); - codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter); - codeBlock.add(" }\n"); - } - } - } - } - - // 6. 处理显式配置的字段映射 - for (Map.Entry mapping : fieldMappings.entrySet()) { - String sourceFieldName = mapping.getKey(); - String targetFieldName = mapping.getValue(); - - if (ignoredFields.contains(sourceFieldName)) continue; - - VariableElement sourceField = sourceFields.get(sourceFieldName); - VariableElement targetField = targetFields.get(targetFieldName); - - if (sourceField == null) { - messager.printMessage(Diagnostic.Kind.WARNING, "源字段不存在: " + sourceFieldName, sourceTypeElement); - continue; - } - - if (targetField == null) { - messager.printMessage(Diagnostic.Kind.WARNING, "目标字段不存在: " + targetFieldName, targetTypeElement); - continue; - } - - if (!isTypeCompatible(sourceField.asType(), targetField.asType())) { - messager.printMessage(Diagnostic.Kind.WARNING, "字段类型不兼容: " + sourceFieldName + " -> " + targetFieldName, sourceTypeElement); - continue; - } - - String sourceGetter = getAccessorName(sourceField, true); - String targetSetter = getAccessorName(targetField, false); - if (copyNulls) { - codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter); - } else { - codeBlock.add(" if (null != source.$L()) {\n", sourceGetter); - codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter); - codeBlock.add(" }\n"); - } - } - codeBlock.add("}"); - builder.addCode(codeBlock.build()); - } - - /** - * 检查类型兼容性: - * 1. 完全相同类型 - * 2. 基本类型与对应包装类型 - * 3. 兼容类型映射(如String和CharSequence) - * 4. 子类到父类的赋值 - */ - private boolean isTypeCompatible(TypeMirror sourceType, TypeMirror targetType) { - // 1. 类型完全相同 - if (typeUtils.isSameType(sourceType, targetType)) { - return true; - } - // 2. 基本类型与包装类型的兼容 - String sourceName = sourceType.toString(); - String targetName = targetType.toString(); - // 基本类型到包装类型 - if (primitiveToWrapper.containsKey(sourceName) && - primitiveToWrapper.get(sourceName).equals(targetName)) { - return true; - } - // 包装类型到基本类型 - if (primitiveToWrapper.containsKey(targetName) && - primitiveToWrapper.get(targetName).equals(sourceName)) { - return true; - } - // 3. 兼容类型映射 - Set sourceCompatible = compatibleTypes.get(sourceName); - if (sourceCompatible != null && sourceCompatible.contains(targetName)) { - return true; - } - Set targetCompatible = compatibleTypes.get(targetName); - if (targetCompatible != null && targetCompatible.contains(sourceName)) { - return true; - } - // 4. 子类到父类的赋值 - if (typeUtils.isAssignable(sourceType, targetType)) { - return true; - } - // 5. 数组类型兼容(长度相同的基本类型数组) - if (sourceType.getKind() == TypeKind.ARRAY && - targetType.getKind() == TypeKind.ARRAY) { - TypeMirror sourceComponent = ((ArrayType) sourceType).getComponentType(); - TypeMirror targetComponent = ((ArrayType) targetType).getComponentType(); - return isTypeCompatible(sourceComponent, targetComponent); - } - return false; - } - - /** - * 获取字段的访问器方法名 - * @param field 字段元素 - * @param isGetter true: getter方法, false: setter方法 - */ - private String getAccessorName(VariableElement field, boolean isGetter) { - String fieldName = field.getSimpleName().toString(); - String prefix = isGetter ? (field.asType().getKind() == TypeKind.BOOLEAN ? "is" : "get") : "set"; - return prefix + capitalize(fieldName); - } - - private String capitalize(String str) { - return Character.toUpperCase(str.charAt(0)) + str.substring(1); - } - - private void collectUsageInstructions(TypeElement enclosingClass, - List methods, - String helperClassName) { - String className = enclosingClass.getQualifiedName().toString(); - List instructions = new ArrayList<>(); - - instructions.add("自动生成的对象拷贝Copier类: " + helperClassName); - instructions.add("支持自动映射:"); - instructions.add(" - 同名同类型字段"); - instructions.add(" - 基本类型与包装类型"); - instructions.add(" - 常用类型转换(String/CharSequence, Date/时间类等)"); - instructions.add("请在以下方法中调用对应的Helper方法:"); - - for (ExecutableElement method : methods) { - String methodName = method.getSimpleName().toString(); - String helperMethodName = methodName; - - instructions.add(String.format(" - 方法 %s::%s 替换为: %s.%s(source, target)", - className, - methodName, - helperClassName, - helperMethodName - )); - } - - usageInstructions.put(className, instructions); - } - - private void printUsageInstructions() { - if (usageInstructions.isEmpty()) return; - - messager.printMessage(Diagnostic.Kind.NOTE, "============================================================"); - messager.printMessage(Diagnostic.Kind.NOTE, " 增强版对象拷贝助手使用说明"); - messager.printMessage(Diagnostic.Kind.NOTE, "============================================================"); - messager.printMessage(Diagnostic.Kind.NOTE, "支持特性:"); - messager.printMessage(Diagnostic.Kind.NOTE, "1. 同名同类型字段自动复制"); - messager.printMessage(Diagnostic.Kind.NOTE, "2. 基本类型与包装类型自动兼容"); - messager.printMessage(Diagnostic.Kind.NOTE, "3. 常用类型自动转换:"); - messager.printMessage(Diagnostic.Kind.NOTE, " - String ↔ CharSequence"); - messager.printMessage(Diagnostic.Kind.NOTE, " - java.util.Date ↔ java.time.*"); - messager.printMessage(Diagnostic.Kind.NOTE, " - 子类到父类的赋值"); - messager.printMessage(Diagnostic.Kind.NOTE, "4. 显式字段映射配置"); - messager.printMessage(Diagnostic.Kind.NOTE, "5. 空值处理控制"); - messager.printMessage(Diagnostic.Kind.NOTE, "------------------------------------------------------------"); - for (Map.Entry> entry : usageInstructions.entrySet()) { - messager.printMessage(Diagnostic.Kind.NOTE, "处理类: " + entry.getKey()); - for (String instruction : entry.getValue()) { - messager.printMessage(Diagnostic.Kind.NOTE, instruction); - } - messager.printMessage(Diagnostic.Kind.NOTE, ""); - } - messager.printMessage(Diagnostic.Kind.NOTE, "操作步骤:"); - messager.printMessage(Diagnostic.Kind.NOTE, "1. 打开被@ObjectCopy注解的方法"); - messager.printMessage(Diagnostic.Kind.NOTE, "2. 将方法体替换为对应的Helper方法调用"); - messager.printMessage(Diagnostic.Kind.NOTE, "3. 保存并重新编译"); - messager.printMessage(Diagnostic.Kind.NOTE, "============================================================"); - } -} +//package com.acanx.util.incubator.annotation; +// +//import com.acanx.annotation.ObjectCopy; +//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.SupportedAnnotationTypes; +//import javax.annotation.processing.SupportedOptions; +//import javax.annotation.processing.SupportedSourceVersion; +//import javax.lang.model.SourceVersion; +//import javax.lang.model.element.Element; +//import javax.lang.model.element.ElementKind; +//import javax.lang.model.element.ExecutableElement; +//import javax.lang.model.element.Modifier; +//import javax.lang.model.element.TypeElement; +//import javax.lang.model.element.VariableElement; +//import javax.lang.model.type.ArrayType; +//import javax.lang.model.type.TypeKind; +//import javax.lang.model.type.TypeMirror; +//import javax.lang.model.util.Elements; +//import javax.tools.Diagnostic; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.HashMap; +//import java.util.HashSet; +//import java.util.List; +//import java.util.Map; +//import java.util.Set; +//import java.util.stream.Collectors; +//import javax.lang.model.util.Types; +// +//import com.squareup.javapoet.CodeBlock; +//import com.squareup.javapoet.JavaFile; +//import com.squareup.javapoet.MethodSpec; +//import com.squareup.javapoet.TypeName; +//import com.squareup.javapoet.TypeSpec; +// +///** +// * ObjectCopyProcessor +// * +// * @author ACANX +// * @since 202506 +// */ +//@SupportedAnnotationTypes("com.acanx.annotation.ObjectCopy") +//@SupportedSourceVersion(SourceVersion.RELEASE_17) +//@SupportedOptions({"incremental"}) // 支持增量编译 +//public class ObjectCopyProcessor extends AbstractProcessor { +// +// public static final String SUFFIX = "Copier"; +// private Messager messager; +// private Filer filer; +// private Elements elementUtils; +// private Types typeUtils; +// +// // 全局跟踪已生成的文件 +// private static final Set generatedFiles = new HashSet<>(); +// +// // 收集使用说明 +// private static final Map> usageInstructions = new HashMap<>(); +// +// // 基本类型与包装类型映射 +// private static final Map primitiveToWrapper = Map.of( +// "int", "java.lang.Integer", +// "long", "java.lang.Long", +// "double", "java.lang.Double", +// "float", "java.lang.Float", +// "boolean", "java.lang.Boolean", +// "char", "java.lang.Character", +// "byte", "java.lang.Byte", +// "short", "java.lang.Short" +// ); +// +// // 兼容类型映射 +// private static final Map> compatibleTypes = Map.of( +// "java.lang.String", Set.of("java.lang.CharSequence"), +// "java.lang.CharSequence", Set.of("java.lang.String"), +// "java.util.Date", Set.of("java.time.Instant", "java.time.LocalDateTime", "java.time.ZonedDateTime"), +// "java.time.Instant", Set.of("java.util.Date", "java.time.LocalDateTime", "java.time.ZonedDateTime"), +// "java.time.LocalDateTime", Set.of("java.util.Date", "java.time.Instant", "java.time.ZonedDateTime"), +// "java.time.ZonedDateTime", Set.of("java.util.Date", "java.time.Instant", "java.time.LocalDateTime") +// ); +// +// @Override +// public synchronized void init(ProcessingEnvironment processingEnv) { +// super.init(processingEnv); +// messager = processingEnv.getMessager(); +// filer = processingEnv.getFiler(); +// elementUtils = processingEnv.getElementUtils(); +// typeUtils = processingEnv.getTypeUtils(); +// } +// +// @Override +// public boolean process(Set annotations, RoundEnvironment roundEnv) { +// try { +// if (roundEnv.processingOver()) { +// printUsageInstructions(); +// return false; +// } +// +// Set elements = roundEnv.getElementsAnnotatedWith(ObjectCopy.class); +// List methods = elements.stream() +// .filter(e -> e.getKind() == ElementKind.METHOD) +// .map(e -> (ExecutableElement) e) +// .collect(Collectors.toList()); +// +// if (methods.isEmpty()) return false; +// +// Map> methodsByClass = methods.stream() +// .collect(Collectors.groupingBy( +// method -> (TypeElement) method.getEnclosingElement() +// )); +// +// for (Map.Entry> entry : methodsByClass.entrySet()) { +// TypeElement enclosingClass = entry.getKey(); +// List classMethods = entry.getValue(); +// String helperClassName = enclosingClass.getSimpleName().toString() + SUFFIX; +// +// createCopierClass(enclosingClass, classMethods, helperClassName); +// collectUsageInstructions(enclosingClass, classMethods, helperClassName); +// } +// +// return true; +// } catch (Exception e) { +// messager.printMessage(Diagnostic.Kind.ERROR, "处理器异常: " + e.getMessage()); +// return false; +// } +// } +// +// private void createCopierClass(TypeElement enclosingClass, +// List methods, +// String helperClassName) { +// try { +// String packageName = elementUtils.getPackageOf(enclosingClass).toString(); +// String fullClassName = packageName + "." + helperClassName; +// +// if (generatedFiles.contains(fullClassName)) return; +// +// TypeSpec.Builder helperClassBuilder = TypeSpec.classBuilder(helperClassName) +// .addModifiers(Modifier.PUBLIC, Modifier.FINAL) +// .addJavadoc("Incubator-Annotation生成的对象拷贝Copier类\n\n") +// .addJavadoc("支持功能:\n") +// .addJavadoc("- 同名同类型字段自动复制\n") +// .addJavadoc("- 基本类型与包装类型兼容\n") +// .addJavadoc("- 常用类型自动转换\n") +// .addJavadoc("- 字段映射配置\n\n") +// .addJavadoc("方法列表:\n"); +// +// for (ExecutableElement method : methods) { +// String methodName = method.getSimpleName().toString(); +// String helperMethodName = methodName; +// ObjectCopy annotation = method.getAnnotation(ObjectCopy.class); +// +// helperClassBuilder.addJavadoc("- $L()\n", helperMethodName); +// +// MethodSpec helperMethod = generateCopyMethod(method, helperMethodName, annotation); +// if (helperMethod != null) { +// helperClassBuilder.addMethod(helperMethod); +// } +// } +// +// JavaFile javaFile = JavaFile.builder(packageName, helperClassBuilder.build()) +// .indent(" ") +// .build(); +// +// javaFile.writeTo(filer); +// generatedFiles.add(fullClassName); +// +// } catch (Exception e) { +// messager.printMessage(Diagnostic.Kind.ERROR, "创建Copier类失败: " + e.getMessage(), enclosingClass); +// } +// } +// +// private MethodSpec generateCopyMethod(ExecutableElement method, +// String helperMethodName, +// ObjectCopy annotation) { +// List parameters = method.getParameters(); +// +// if (parameters.size() != 2) { +// messager.printMessage(Diagnostic.Kind.ERROR, "@ObjectCopy 方法必须有两个参数", method); +// return null; +// } +// +// VariableElement sourceParam = parameters.get(0); +// VariableElement targetParam = parameters.get(1); +// TypeElement sourceType = (TypeElement) typeUtils.asElement(sourceParam.asType()); +// TypeElement targetType = (TypeElement) typeUtils.asElement(targetParam.asType()); +// +// if (sourceType == null || targetType == null) { +// messager.printMessage(Diagnostic.Kind.ERROR, "无法解析源或目标类型", method); +// return null; +// } +// +// // 添加方法Javadoc +// CodeBlock.Builder javadocBuilder = CodeBlock.builder(); +// javadocBuilder.add("Incubator-Annotation生成的拷贝方法\n用于替换 {@link $T#$L($T, $T)}\n\n", +// method.getEnclosingElement(), +// method.getSimpleName().toString(), +// sourceParam.asType(), +// targetParam.asType()); +// +// javadocBuilder.add("配置信息:\n"); +// javadocBuilder.add("- copyNulls: $L\n", annotation.copyNulls()); +// javadocBuilder.add("- 忽略字段: $L\n", Arrays.toString(annotation.ignoreFields())); +// +// if (annotation.fieldMappings().length > 0) { +// javadocBuilder.add("- 字段映射:\n"); +// for (ObjectCopy.FieldMapping mapping : annotation.fieldMappings()) { +// javadocBuilder.add(" - $L → $L\n", mapping.s(), mapping.t()); +// } +// } +// +// // 添加参数文档 +// javadocBuilder.add("\n@param source 源对象,包含待拷贝的数据\n"); +// javadocBuilder.add("@param target 目标对象,接收拷贝后的数据\n"); +// +// MethodSpec.Builder builder = MethodSpec.methodBuilder(helperMethodName) +// .addModifiers(Modifier.PUBLIC, Modifier.STATIC) +// .returns(void.class) +// .addJavadoc(javadocBuilder.build()) +// .addParameter(TypeName.get(sourceParam.asType()), "source") +// .addParameter(TypeName.get(targetParam.asType()), "target"); +// +// addFieldCopyStatements(builder, annotation, sourceType, targetType); +// return builder.build(); +// } +// +// private void addFieldCopyStatements(MethodSpec.Builder builder, +// ObjectCopy config, +// TypeElement sourceTypeElement, +// TypeElement targetTypeElement) { +// +// boolean copyNulls = config.copyNulls(); +// Set ignoredFields = new HashSet<>(Arrays.asList(config.ignoreFields())); +// +// // 1. 准备字段映射 +// Map fieldMappings = new HashMap<>(); +// for (ObjectCopy.FieldMapping mapping : config.fieldMappings()) { +// fieldMappings.put(mapping.s(), mapping.t()); +// } +// +// // 2. 收集源类字段 +// Map sourceFields = new HashMap<>(); +// for (Element element : sourceTypeElement.getEnclosedElements()) { +// if (element.getKind() == ElementKind.FIELD) { +// VariableElement field = (VariableElement) element; +// sourceFields.put(field.getSimpleName().toString(), field); +// } +// } +// +// // 3. 收集目标类字段 +// Map targetFields = new HashMap<>(); +// for (Element element : targetTypeElement.getEnclosedElements()) { +// if (element.getKind() == ElementKind.FIELD) { +// VariableElement field = (VariableElement) element; +// targetFields.put(field.getSimpleName().toString(), field); +// } +// } +// +// // 4. 使用代码块构建拷贝逻辑 +// CodeBlock.Builder codeBlock = CodeBlock.builder(); +// codeBlock.add("if (null != source && null != target) {\n"); +// +// // 5. 处理自动映射的同名字段 +// for (String sourceFieldName : sourceFields.keySet()) { +// if (ignoredFields.contains(sourceFieldName)) continue; +// if (fieldMappings.containsKey(sourceFieldName)) continue; +// +// VariableElement sourceField = sourceFields.get(sourceFieldName); +// VariableElement targetField = targetFields.get(sourceFieldName); +// +// if (targetField != null) { +// if (isTypeCompatible(sourceField.asType(), targetField.asType())) { +// String sourceGetter = getAccessorName(sourceField, true); +// String targetSetter = getAccessorName(targetField, false); +// +// if (copyNulls) { +// codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter); +// } else { +// codeBlock.add(" if (null != source.$L()) {\n", sourceGetter); +// codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter); +// codeBlock.add(" }\n"); +// } +// } +// } +// } +// +// // 6. 处理显式配置的字段映射 +// for (Map.Entry mapping : fieldMappings.entrySet()) { +// String sourceFieldName = mapping.getKey(); +// String targetFieldName = mapping.getValue(); +// +// if (ignoredFields.contains(sourceFieldName)) continue; +// +// VariableElement sourceField = sourceFields.get(sourceFieldName); +// VariableElement targetField = targetFields.get(targetFieldName); +// +// if (sourceField == null) { +// messager.printMessage(Diagnostic.Kind.WARNING, "源字段不存在: " + sourceFieldName, sourceTypeElement); +// continue; +// } +// +// if (targetField == null) { +// messager.printMessage(Diagnostic.Kind.WARNING, "目标字段不存在: " + targetFieldName, targetTypeElement); +// continue; +// } +// +// if (!isTypeCompatible(sourceField.asType(), targetField.asType())) { +// messager.printMessage(Diagnostic.Kind.WARNING, "字段类型不兼容: " + sourceFieldName + " -> " + targetFieldName, sourceTypeElement); +// continue; +// } +// +// String sourceGetter = getAccessorName(sourceField, true); +// String targetSetter = getAccessorName(targetField, false); +// if (copyNulls) { +// codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter); +// } else { +// codeBlock.add(" if (null != source.$L()) {\n", sourceGetter); +// codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter); +// codeBlock.add(" }\n"); +// } +// } +// codeBlock.add("}"); +// builder.addCode(codeBlock.build()); +// } +// +// /** +// * 检查类型兼容性: +// * 1. 完全相同类型 +// * 2. 基本类型与对应包装类型 +// * 3. 兼容类型映射(如String和CharSequence) +// * 4. 子类到父类的赋值 +// */ +// private boolean isTypeCompatible(TypeMirror sourceType, TypeMirror targetType) { +// // 1. 类型完全相同 +// if (typeUtils.isSameType(sourceType, targetType)) { +// return true; +// } +// // 2. 基本类型与包装类型的兼容 +// String sourceName = sourceType.toString(); +// String targetName = targetType.toString(); +// // 基本类型到包装类型 +// if (primitiveToWrapper.containsKey(sourceName) && +// primitiveToWrapper.get(sourceName).equals(targetName)) { +// return true; +// } +// // 包装类型到基本类型 +// if (primitiveToWrapper.containsKey(targetName) && +// primitiveToWrapper.get(targetName).equals(sourceName)) { +// return true; +// } +// // 3. 兼容类型映射 +// Set sourceCompatible = compatibleTypes.get(sourceName); +// if (sourceCompatible != null && sourceCompatible.contains(targetName)) { +// return true; +// } +// Set targetCompatible = compatibleTypes.get(targetName); +// if (targetCompatible != null && targetCompatible.contains(sourceName)) { +// return true; +// } +// // 4. 子类到父类的赋值 +// if (typeUtils.isAssignable(sourceType, targetType)) { +// return true; +// } +// // 5. 数组类型兼容(长度相同的基本类型数组) +// if (sourceType.getKind() == TypeKind.ARRAY && +// targetType.getKind() == TypeKind.ARRAY) { +// TypeMirror sourceComponent = ((ArrayType) sourceType).getComponentType(); +// TypeMirror targetComponent = ((ArrayType) targetType).getComponentType(); +// return isTypeCompatible(sourceComponent, targetComponent); +// } +// return false; +// } +// +// /** +// * 获取字段的访问器方法名 +// * @param field 字段元素 +// * @param isGetter true: getter方法, false: setter方法 +// */ +// private String getAccessorName(VariableElement field, boolean isGetter) { +// String fieldName = field.getSimpleName().toString(); +// String prefix = isGetter ? (field.asType().getKind() == TypeKind.BOOLEAN ? "is" : "get") : "set"; +// return prefix + capitalize(fieldName); +// } +// +// private String capitalize(String str) { +// return Character.toUpperCase(str.charAt(0)) + str.substring(1); +// } +// +// private void collectUsageInstructions(TypeElement enclosingClass, +// List methods, +// String helperClassName) { +// String className = enclosingClass.getQualifiedName().toString(); +// List instructions = new ArrayList<>(); +// +// instructions.add("自动生成的对象拷贝Copier类: " + helperClassName); +// instructions.add("支持自动映射:"); +// instructions.add(" - 同名同类型字段"); +// instructions.add(" - 基本类型与包装类型"); +// instructions.add(" - 常用类型转换(String/CharSequence, Date/时间类等)"); +// instructions.add("请在以下方法中调用对应的Helper方法:"); +// +// for (ExecutableElement method : methods) { +// String methodName = method.getSimpleName().toString(); +// String helperMethodName = methodName; +// +// instructions.add(String.format(" - 方法 %s::%s 替换为: %s.%s(source, target)", +// className, +// methodName, +// helperClassName, +// helperMethodName +// )); +// } +// +// usageInstructions.put(className, instructions); +// } +// +// private void printUsageInstructions() { +// if (usageInstructions.isEmpty()) return; +// +// messager.printMessage(Diagnostic.Kind.NOTE, "============================================================"); +// messager.printMessage(Diagnostic.Kind.NOTE, " 增强版对象拷贝助手使用说明"); +// messager.printMessage(Diagnostic.Kind.NOTE, "============================================================"); +// messager.printMessage(Diagnostic.Kind.NOTE, "支持特性:"); +// messager.printMessage(Diagnostic.Kind.NOTE, "1. 同名同类型字段自动复制"); +// messager.printMessage(Diagnostic.Kind.NOTE, "2. 基本类型与包装类型自动兼容"); +// messager.printMessage(Diagnostic.Kind.NOTE, "3. 常用类型自动转换:"); +// messager.printMessage(Diagnostic.Kind.NOTE, " - String ↔ CharSequence"); +// messager.printMessage(Diagnostic.Kind.NOTE, " - java.util.Date ↔ java.time.*"); +// messager.printMessage(Diagnostic.Kind.NOTE, " - 子类到父类的赋值"); +// messager.printMessage(Diagnostic.Kind.NOTE, "4. 显式字段映射配置"); +// messager.printMessage(Diagnostic.Kind.NOTE, "5. 空值处理控制"); +// messager.printMessage(Diagnostic.Kind.NOTE, "------------------------------------------------------------"); +// for (Map.Entry> entry : usageInstructions.entrySet()) { +// messager.printMessage(Diagnostic.Kind.NOTE, "处理类: " + entry.getKey()); +// for (String instruction : entry.getValue()) { +// messager.printMessage(Diagnostic.Kind.NOTE, instruction); +// } +// messager.printMessage(Diagnostic.Kind.NOTE, ""); +// } +// messager.printMessage(Diagnostic.Kind.NOTE, "操作步骤:"); +// messager.printMessage(Diagnostic.Kind.NOTE, "1. 打开被@ObjectCopy注解的方法"); +// messager.printMessage(Diagnostic.Kind.NOTE, "2. 将方法体替换为对应的Helper方法调用"); +// messager.printMessage(Diagnostic.Kind.NOTE, "3. 保存并重新编译"); +// messager.printMessage(Diagnostic.Kind.NOTE, "============================================================"); +// } +//} diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/PluginLoaderTest.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/PluginLoaderTest.java deleted file mode 100644 index 76f601a..0000000 --- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/PluginLoaderTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.acanx.util.incubator.annotation;//package com.acanx.util.object.copy; -// -//import com.sun.source.util.Plugin; -// -//import java.util.ServiceLoader; -// -///** -// * PluginLoaderTest -// * -// * @author ACANX -// * @date 2025-06-14 -// * @since 202506 -// */ -//public class PluginLoaderTest { -// public static void main(String[] args) { -// System.out.println("=== 开始加载插件 ==="); -// System.out.println("类路径: " + System.getProperty("java.class.path")); -// -// ServiceLoader loader = ServiceLoader.load(Plugin.class); -// int count = 0; -// -// for (Plugin plugin : loader) { -// System.out.println("✅ 找到插件: " + plugin.getClass().getName()); -// System.out.println(" 插件名称: " + plugin.getName()); -// count++; -// } -// -// if (count == 0) { -// System.err.println("❌ 未找到任何插件"); -// -// // 尝试直接加载类 -// try { -// Class clazz = Class.forName("com.acanx.util.object.copy.MetaObjectCopyPlugin"); -// System.out.println("ℹ️ 类存在: " + clazz); -// -// // 尝试实例化 -// Plugin plugin = (Plugin) clazz.getDeclaredConstructor().newInstance(); -// System.out.println("✅ 手动创建插件成功: " + plugin.getName()); -// } catch (ClassNotFoundException e) { -// System.err.println("❌ 类未找到: com.acanx.util.object.copy.MetaObjectCopyPlugin"); -// } catch (Exception e) { -// System.err.println("❌ 创建实例失败: " + e.getMessage()); -// } -// } -// } -//} diff --git a/autil-incubator/incubator-annotation/src/main/java/module-info.java b/autil-incubator/incubator-annotation/src/main/java/module-info.java new file mode 100644 index 0000000..e8f79f2 --- /dev/null +++ b/autil-incubator/incubator-annotation/src/main/java/module-info.java @@ -0,0 +1,17 @@ +import com.acanx.util.incubator.annotation.CopierProcessor; + +module com.acanx.util.incubator.annotation { + + + requires java.compiler; + requires static jdk.compiler; +// requires model.test; + + + // 导出注解处理器所在的包 + exports com.acanx.util.incubator.annotation; + // 关键修正:打开包到jdk.compiler模块 允许反射访问 + opens com.acanx.util.incubator.annotation to jdk.compiler; + + provides javax.annotation.processing.Processor with CopierProcessor; +} \ No newline at end of file diff --git a/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor index affd371..bb7a6e7 100644 --- a/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -1 +1 @@ -com.acanx.util.incubator.annotation.ObjectCopyProcessor +com.acanx.util.incubator.annotation.CopierProcessor diff --git a/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/AppTest.java b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/AppTest.java index 3b4b535..5a07649 100644 --- a/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/AppTest.java +++ b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/AppTest.java @@ -1,20 +1,17 @@ package com.acanx.meta.model; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - /** * Unit test for simple App. */ public class AppTest { - /** - * Rigorous Test :-) - */ - @Test - public void shouldAnswerWithTrue() { - Assertions.assertTrue(true); - } +// /** +// * Rigorous Test :-) +// */ +// @Test +// public void shouldAnswerWithTrue() { +// Assertions.assertTrue(true); +// } } \ No newline at end of file diff --git a/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java new file mode 100644 index 0000000..04b77b0 --- /dev/null +++ b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java @@ -0,0 +1,42 @@ +//package com.acanx.meta.model; +// +//import com.acanx.meta.model.test.annotation.model.MessageFlex; +//import com.acanx.meta.model.test.annotation.model.MessageStable; +//import com.acanx.util.incubator.annotation.Copier; +// +///** +// * CopyTest +// * +// * @author ACANX +// * @since 20251110 +// */ +//public class CopyTest { +// +// +// +// @Copier +// private void convert(MessageFlex flex, MessageStable stable) {}; +// +// +//// @ObjectCopier(ignoreNull = true, exclude = {"internalId"}) +//// private void convert1(MessageFlex flex, MessageStable stable) { +//// // 编译后这个方法会被增强为: +//// // if (flex == null) throw new IllegalArgumentException("Source object cannot be null"); +//// // if (stable == null) throw new IllegalArgumentException("Target object cannot be null"); +//// // stable.content = flex.content != null ? flex.content : stable.content; +//// // stable.priority = flex.priority != null ? flex.priority : stable.priority; +//// // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent; +//// // stable.tags = flex.tags != null ? flex.tags : stable.tags; +//// } +// +//// @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP) +//// private void deepConvert(MessageFlex flex, MessageStable stable) { +//// // 深拷贝版本的转换 +//// } +// +// public void processConversion(MessageFlex flex, MessageStable stable) { +// convert(flex, stable); +// // 其他业务逻辑 +// } +// +//} diff --git a/autil-incubator/pom.xml b/autil-incubator/pom.xml index 092ec27..1a8cc27 100644 --- a/autil-incubator/pom.xml +++ b/autil-incubator/pom.xml @@ -16,14 +16,13 @@ https://github.com/ACANX/AUtil - 11 + 21 UTF-8 - 11 3.14.1 incubator-core - + incubator-annotation diff --git a/autil-test/pom.xml b/autil-test/pom.xml index f5e6816..f24e0dc 100644 --- a/autil-test/pom.xml +++ b/autil-test/pom.xml @@ -36,6 +36,25 @@ ${revision} test + + + com.acanx.util + incubator-annotation + ${revision} + test + + + com.acanx.meta.model + model-test + 0.5.7-SNAPSHOT + + + com.acanx.util + incubator-annotation + 0.4.3-SNAPSHOT + compile + + @@ -47,7 +66,33 @@ ${java.version} ${java.version} + + --add-exports + jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + --add-exports + jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + + + com.acanx.util.incubator.annotation.CopierProcessor + + ${project.build.directory}/generated-sources/annotations + + + com.acanx.util + incubator-annotation + ${revision} + + + diff --git a/autil-test/src/main/java/com/acanx/util/enums/Copy2.java b/autil-test/src/main/java/com/acanx/util/enums/Copy2.java new file mode 100644 index 0000000..e004f13 --- /dev/null +++ b/autil-test/src/main/java/com/acanx/util/enums/Copy2.java @@ -0,0 +1,27 @@ +package com.acanx.util.enums; + + +import com.acanx.meta.model.test.annotation.model.MessageFlex; +import com.acanx.meta.model.test.annotation.model.MessageStable; +import com.acanx.util.incubator.annotation.Copier; + +/** + * CopyTest + * + * @author ACANX + * @since 20251110 + */ +public class Copy2 { + + + + @Copier + private void flexToStable(MessageFlex flex, MessageStable stable) {}; + + + @Copier + private void flex2ToStable(MessageFlex flex, MessageStable stable) {}; + + + +} diff --git a/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java b/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java new file mode 100644 index 0000000..888b2c3 --- /dev/null +++ b/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java @@ -0,0 +1,42 @@ +package com.acanx.util.annotation; + +import com.acanx.meta.model.test.annotation.model.MessageFlex; +import com.acanx.meta.model.test.annotation.model.MessageStable; +import com.acanx.util.incubator.annotation.Copier; + +/** + * CopyTest + * + * @author ACANX + * @since 20251110 + */ +public class CopyTest { + + + + @Copier + private void convert(MessageFlex flex, MessageStable stable) {}; + + + @Copier(ignoreNull = true, exclude = {"internalId"}) + private void convert1(MessageFlex flex, MessageStable stable) { + // 编译后这个方法会被增强为: + // if (flex == null) throw new IllegalArgumentException("Source object cannot be null"); + // if (stable == null) throw new IllegalArgumentException("Target object cannot be null"); + // stable.content = flex.content != null ? flex.content : stable.content; + // stable.priority = flex.priority != null ? flex.priority : stable.priority; + // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent; + // stable.tags = flex.tags != null ? flex.tags : stable.tags; + } + + @Copier(strategy = Copier.CopyStrategy.DEEP) + private void deepConvert(MessageFlex flex, MessageStable stable) { + // 深拷贝版本的转换 + } + + public void processConversion(MessageFlex flex, MessageStable stable) { + convert(flex, stable); + // 其他业务逻辑 + } + +} diff --git a/pom.xml b/pom.xml index 210e416..56dfcbd 100644 --- a/pom.xml +++ b/pom.xml @@ -72,20 +72,20 @@ - - - sonatype-snapshots - Sonatype Snapshot Repository - https://central.sonatype.com/repository/maven-snapshots/ - - false - - - true - always - - - + + + + + + + + + + + + + +