xiejun
2024-11-01 80b6cbfc9c861469146318d0b3dd5f8b8b525b8a
Source/BladeX-Tool/blade-core-auto/src/main/java/org/springblade/core/auto/service/AutoServiceProcessor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,254 @@
/*
 *      Copyright (c) 2018-2028, DreamLu All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: DreamLu å¢æ˜¥æ¢¦ (596392912@qq.com)
 */
package org.springblade.core.auto.service;
import org.springblade.core.auto.common.AbstractBladeProcessor;
import org.springblade.core.auto.common.MultiSetMap;
import org.springblade.core.auto.common.Sets;
import org.springblade.core.auto.common.TypeHelper;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.lang.model.util.Types;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Collectors;
/**
 * java spi æœåŠ¡è‡ªåŠ¨å¤„ç†å™¨ å‚考:google auto
 *
 * @author L.cm
 */
@SupportedOptions("debug")
public class AutoServiceProcessor extends AbstractBladeProcessor {
   /**
    * spi æœåŠ¡é›†åˆï¼Œkey æŽ¥å£ -> value å®žçŽ°åˆ—è¡¨
    */
   private final MultiSetMap<String, String> providers = new MultiSetMap<>();
   private TypeHelper typeHelper;
   @Override
   public synchronized void init(ProcessingEnvironment env) {
      super.init(env);
      this.typeHelper = new TypeHelper(env);
   }
   @Override
   public Set<String> getSupportedAnnotationTypes() {
      return Sets.ofImmutableSet(AutoService.class.getName());
   }
   @Override
   protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
      if (roundEnv.processingOver()) {
         generateConfigFiles();
      } else {
         processAnnotations(annotations, roundEnv);
      }
      return true;
   }
   private void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
      Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
      log(annotations.toString());
      log(elements.toString());
      for (Element e : elements) {
         TypeElement providerImplementer = (TypeElement) e;
         AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class);
         if (annotationMirror == null) {
            continue;
         }
         Set<TypeMirror> typeMirrors = getValueFieldOfClasses(annotationMirror);
         if (typeMirrors.isEmpty()) {
            error("No service interfaces provided for element!", e, annotationMirror);
            continue;
         }
         for (TypeMirror typeMirror : typeMirrors) {
            String providerInterfaceName = typeHelper.getType(typeMirror);
            Name providerImplementerName = providerImplementer.getQualifiedName();
            log("provider interface: " + providerInterfaceName);
            log("provider implementer: " + providerImplementerName);
            if (checkImplementer(providerImplementer, typeMirror)) {
               providers.put(providerInterfaceName, typeHelper.getType(providerImplementer));
            } else {
               String message = "ServiceProviders must implement their service provider interface. "
                  + providerImplementerName + " does not implement " + providerInterfaceName;
               error(message, e, annotationMirror);
            }
         }
      }
   }
   private void generateConfigFiles() {
      Filer filer = processingEnv.getFiler();
      for (String providerInterface : providers.keySet()) {
         String resourceFile = "META-INF/services/" + providerInterface;
         log("Working on resource file: " + resourceFile);
         try {
            SortedSet<String> allServices = new TreeSet<>();
            try {
               FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
               log("Looking for existing resource file at " + existingFile.toUri());
               Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
               log("Existing service entries: " + oldServices);
               allServices.addAll(oldServices);
            } catch (IOException e) {
               log("Resource file did not already exist.");
            }
            Set<String> newServices = new HashSet<>(providers.get(providerInterface));
            if (allServices.containsAll(newServices)) {
               log("No new service entries being added.");
               return;
            }
            allServices.addAll(newServices);
            log("New service file contents: " + allServices);
            FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
            OutputStream out = fileObject.openOutputStream();
            ServicesFiles.writeServiceFile(allServices, out);
            out.close();
            log("Wrote to: " + fileObject.toUri());
         } catch (IOException e) {
            fatalError("Unable to create " + resourceFile + ", " + e);
            return;
         }
      }
   }
   /**
    * Verifies {@link java.util.spi.LocaleServiceProvider} constraints on the concrete provider class.
    * Note that these constraints are enforced at runtime via the ServiceLoader,
    * we're just checking them at compile time to be extra nice to our users.
    */
   private boolean checkImplementer(TypeElement providerImplementer, TypeMirror providerType) {
      // TODO: We're currently only enforcing the subtype relationship
      // constraint. It would be nice to enforce them all.
      Types types = processingEnv.getTypeUtils();
      return types.isSubtype(providerImplementer.asType(), providerType);
   }
   /**
    * è¯»å– AutoService ä¸Šçš„ value å€¼
    *
    * @param annotationMirror AnnotationMirror
    * @return value é›†åˆ
    */
   private Set<TypeMirror> getValueFieldOfClasses(AnnotationMirror annotationMirror) {
      return getAnnotationValue(annotationMirror, "value")
         .accept(new SimpleAnnotationValueVisitor8<Set<TypeMirror>, Void>() {
            @Override
            public Set<TypeMirror> visitType(TypeMirror typeMirror, Void v) {
               Set<TypeMirror> declaredTypeSet = new HashSet<>(1);
               declaredTypeSet.add(typeMirror);
               return Collections.unmodifiableSet(declaredTypeSet);
            }
            @Override
            public Set<TypeMirror> visitArray(
               List<? extends AnnotationValue> values, Void v) {
               return values
                  .stream()
                  .flatMap(value -> value.accept(this, null).stream())
                  .collect(Collectors.toSet());
            }
         }, null);
   }
   /**
    * Returns a {@link ExecutableElement} and its associated {@link AnnotationValue} if such
    * an element was either declared in the usage represented by the provided
    * {@link AnnotationMirror}, or if such an element was defined with a default.
    *
    * @param annotationMirror AnnotationMirror
    * @param elementName      elementName
    * @return AnnotationValue map
    * @throws IllegalArgumentException if no element is defined with the given elementName.
    */
   public AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String elementName) {
      Objects.requireNonNull(annotationMirror);
      Objects.requireNonNull(elementName);
      for (Map.Entry<ExecutableElement, AnnotationValue> entry : getAnnotationValuesWithDefaults(annotationMirror).entrySet()) {
         if (entry.getKey().getSimpleName().contentEquals(elementName)) {
            return entry.getValue();
         }
      }
      String name = typeHelper.getType(annotationMirror);
      throw new IllegalArgumentException(String.format("@%s does not define an element %s()", name, elementName));
   }
   /**
    * Returns the {@link AnnotationMirror}'s map of {@link AnnotationValue} indexed by {@link
    * ExecutableElement}, supplying default values from the annotation if the annotation property has
    * not been set. This is equivalent to {@link
    * Elements#getElementValuesWithDefaults(AnnotationMirror)} but can be called statically without
    * an {@link Elements} instance.
    *
    * <p>The iteration order of elements of the returned map will be the order in which the {@link
    * ExecutableElement}s are defined in {@code annotation}'s {@linkplain
    * AnnotationMirror#getAnnotationType() type}.
    *
    * @param annotation AnnotationMirror
    * @return AnnotationValue Map
    */
   public Map<ExecutableElement, AnnotationValue> getAnnotationValuesWithDefaults(AnnotationMirror annotation) {
      Map<ExecutableElement, AnnotationValue> values = new HashMap<>(32);
      Map<? extends ExecutableElement, ? extends AnnotationValue> declaredValues = annotation.getElementValues();
      for (ExecutableElement method : ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) {
         // Must iterate and put in this order, to ensure consistency in generated code.
         if (declaredValues.containsKey(method)) {
            values.put(method, declaredValues.get(method));
         } else if (method.getDefaultValue() != null) {
            values.put(method, method.getDefaultValue());
         } else {
            String name = typeHelper.getType(method);
            throw new IllegalStateException(
               "Unset annotation value without default should never happen: " + name + '.' + method.getSimpleName() + "()");
         }
      }
      return Collections.unmodifiableMap(values);
   }
   public AnnotationMirror getAnnotationMirror(Element element, Class<? extends Annotation> annotationClass) {
      String annotationClassName = annotationClass.getCanonicalName();
      for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
         String name = typeHelper.getType(annotationMirror);
         if (name.contentEquals(annotationClassName)) {
            return annotationMirror;
         }
      }
      return null;
   }
}