yangys
2024-04-04 ed4a5236bab800094be4a8378f5098eebe3de6ac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package com.qianwen.smart.core.auto.factories;
 
 
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
 
import com.qianwen.smart.core.auto.annotation.AutoIgnore;
import com.qianwen.smart.core.auto.common.AbstractBladeProcessor;
import com.qianwen.smart.core.auto.common.BootAutoType;
import com.qianwen.smart.core.auto.common.MultiSetMap;
 
 
@SupportedAnnotationTypes({"*"})
@SupportedOptions({"debug"})
public class AutoFactoriesProcessor extends AbstractBladeProcessor {
    private static final String FEIGN_CLIENT_ANNOTATION = "org.springframework.cloud.openfeign.FeignClient";
    private static final String FEIGN_AUTO_CONFIGURE_KEY = "com.qianwen.core.cloud.feign.BladeFeignAutoConfiguration";
    private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final String DEVTOOLS_RESOURCE_LOCATION = "META-INF/spring-devtools.properties";
    private final MultiSetMap<String, String> factories = new MultiSetMap<>();
    private Elements elementUtils;
 
  
  public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    this.elementUtils = processingEnv.getElementUtils();
  }
  
  protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (roundEnv.processingOver()) {
      generateFactoriesFiles();
    } else {
      processAnnotations(annotations, roundEnv);
    } 
    return false;
  }
  
  private void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    log(annotations.toString());
    Set<? extends Element> elementSet = roundEnv.getRootElements();
    log("All Element set: " + elementSet.toString());
    Set<TypeElement> typeElementSet = (Set<TypeElement>)elementSet.stream().filter(this::isClassOrInterface).filter(e -> e instanceof TypeElement).map(e -> (TypeElement)e).collect(Collectors.toSet());
    if (typeElementSet.isEmpty()) {
      log("Annotations elementSet is isEmpty");
      return;
    } 
    for (TypeElement typeElement : typeElementSet) {
      if (isAnnotation(this.elementUtils, typeElement, AutoIgnore.class.getName())) {
        log("Found @AutoIgnore annotationElement: " + typeElement.toString());
        continue;
      } else if (isAnnotation(this.elementUtils, typeElement, FEIGN_CLIENT_ANNOTATION)) {
 
          log("Found @FeignClient Element: " + typeElement.toString());
          ElementKind elementKind = typeElement.getKind();
          if (ElementKind.INTERFACE != elementKind) {
              fatalError("@FeignClient Element " + typeElement.toString() + " 不是接口。");
          } else {
              String factoryName = typeElement.getQualifiedName().toString();
              if (!this.factories.containsVal(factoryName)) {
                  log("读取到新配置 spring.factories factoryName:" + factoryName);
                  this.factories.put(FEIGN_AUTO_CONFIGURE_KEY, factoryName);
              }
          }
 
      }else {
          for (BootAutoType autoType : BootAutoType.values()) {
              String annotation = autoType.getAnnotationName();
              if (isAnnotation(this.elementUtils, typeElement, annotation)) {
                  log("Found @" + annotation + " Element: " + typeElement.toString());
                  String factoryName2 = typeElement.getQualifiedName().toString();
                  if (!this.factories.containsVal(factoryName2)) {
                      log("读取到新配置 spring.factories factoryName:" + factoryName2);
                      this.factories.put(autoType.getConfigureKey(), factoryName2);
                  }
              }
          }
      }
 
      
    } 
  }
  
  private void generateFactoriesFiles() {
      if (this.factories.isEmpty()) {
          return;
      }
      Filer filer = this.processingEnv.getFiler();
      try {
          FileObject factoriesFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", FACTORIES_RESOURCE_LOCATION, new Element[0]);
          FactoriesFiles.writeFactoriesFile(this.factories, factoriesFile.openOutputStream());
          String classesPath = factoriesFile.toUri().toString().split("classes")[0];
          Path projectPath = Paths.get(new URI(classesPath)).getParent();
          String projectName = projectPath.getFileName().toString();
          FileObject devToolsFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", DEVTOOLS_RESOURCE_LOCATION, new Element[0]);
          FactoriesFiles.writeDevToolsFile(projectName, devToolsFile.openOutputStream());
      } catch (IOException | URISyntaxException e) {
          fatalError(e);
      }
 
  }
  
  private boolean isClassOrInterface(Element e) {
    ElementKind kind = e.getKind();
    return (kind == ElementKind.CLASS || kind == ElementKind.INTERFACE);
  }
  
  private boolean isAnnotation(Elements elementUtils, Element e, String annotationFullName) {
    List<? extends AnnotationMirror> annotationList = elementUtils.getAllAnnotationMirrors(e);
    for (AnnotationMirror annotation : annotationList) {
      if (isAnnotation(annotationFullName, annotation))
        return true; 
      Element element = annotation.getAnnotationType().asElement();
      if (element.toString().startsWith("java.lang"))
        continue; 
      if (isAnnotation(elementUtils, element, annotationFullName))
        return true; 
    } 
    return false;
  }
  
  private boolean isAnnotation(String annotationFullName, AnnotationMirror annotation) {
    return annotationFullName.equals(annotation.getAnnotationType().toString());
  }
}