package com.qianwen.smart.core.auto.service; import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Collectors; 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.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.SimpleAnnotationValueVisitor8; import javax.lang.model.util.Types; import javax.tools.FileObject; import javax.tools.StandardLocation; import com.qianwen.smart.core.auto.common.AbstractBladeProcessor; import com.qianwen.smart.core.auto.common.MultiSetMap; import com.qianwen.smart.core.auto.common.Sets; import com.qianwen.smart.core.auto.common.TypeHelper; @SupportedOptions({"debug"}) public class AutoServiceProcessor extends AbstractBladeProcessor { private final MultiSetMap providers = new MultiSetMap<>(); private TypeHelper typeHelper; public synchronized void init(ProcessingEnvironment env) { super.init(env); this.typeHelper = new TypeHelper(env); } public Set getSupportedAnnotationTypes() { return Sets.ofImmutableSet(AutoService.class.getName()); } @Override // org.springblade.core.auto.common.AbstractBladeProcessor protected boolean processImpl(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { generateConfigFiles(); return true; } processAnnotations(annotations, roundEnv); return true; } private void processAnnotations(Set annotations, RoundEnvironment roundEnv) { Set elements = roundEnv.getElementsAnnotatedWith(AutoService.class); log(annotations.toString()); log(elements.toString()); for (Element e : elements) { //Element element = (TypeElement) e; TypeElement element = (TypeElement) e; AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class); if (annotationMirror != null) { Set typeMirrors = getValueFieldOfClasses(annotationMirror); if (typeMirrors.isEmpty()) { error("No service interfaces provided for element!", e, annotationMirror); } else { for (TypeMirror typeMirror : typeMirrors) { String providerInterfaceName = this.typeHelper.getType(typeMirror); Name providerImplementerName = element.getQualifiedName(); log("provider interface: " + providerInterfaceName); log("provider implementer: " + providerImplementerName); if (checkImplementer(element, typeMirror)) { this.providers.put(providerInterfaceName, this.typeHelper.getType(element)); } else { String message = "ServiceProviders must implement their service provider interface. " + providerImplementerName + " does not implement " + providerInterfaceName; error(message, e, annotationMirror); } } } } } } private void generateConfigFiles() { Filer filer = this.processingEnv.getFiler(); for (String providerInterface : this.providers.keySet()) { String resourceFile = "META-INF/services/" + providerInterface; log("Working on resource file: " + resourceFile); try { SortedSet allServices = new TreeSet<>(); try { FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile); log("Looking for existing resource file at " + existingFile.toUri()); Set oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream()); log("Existing service entries: " + oldServices); allServices.addAll(oldServices); } catch (IOException e) { log("Resource file did not already exist."); } Set newServices = new HashSet<>(this.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, new Element[0]); OutputStream out = fileObject.openOutputStream(); ServicesFiles.writeServiceFile(allServices, out); out.close(); log("Wrote to: " + fileObject.toUri()); } catch (IOException e2) { fatalError("Unable to create " + resourceFile + ", " + e2); return; } } } private boolean checkImplementer(TypeElement providerImplementer, TypeMirror providerType) { Types types = this.processingEnv.getTypeUtils(); return types.isSubtype(providerImplementer.asType(), providerType); } private Set getValueFieldOfClasses(AnnotationMirror annotationMirror) { return getAnnotationValue(annotationMirror, "value") .accept(new SimpleAnnotationValueVisitor8, Void>() { public Set visitType(TypeMirror typeMirror, Void v) { Set declaredTypeSet = new HashSet<>(1); declaredTypeSet.add(typeMirror); return Collections.unmodifiableSet(declaredTypeSet); } public Set visitArray(List values, Void v) { return (Set)values .stream() .flatMap(value -> ((Set)value.accept(this, null)).stream()) .collect(Collectors.toSet()); } }, null); } public AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String elementName) { Objects.requireNonNull(annotationMirror); Objects.requireNonNull(elementName); for (Map.Entry entry : getAnnotationValuesWithDefaults(annotationMirror).entrySet()) { if (entry.getKey().getSimpleName().contentEquals(elementName)) { return entry.getValue(); } } String name = this.typeHelper.getType(annotationMirror); throw new IllegalArgumentException(String.format("@%s does not define an element %s()", name, elementName)); } public Map getAnnotationValuesWithDefaults(AnnotationMirror annotation) { Map values = new HashMap<>(32); Map declaredValues = annotation.getElementValues(); for (ExecutableElement method : ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) { if (declaredValues.containsKey(method)) { values.put(method, declaredValues.get(method)); continue; } if (method.getDefaultValue() != null) { values.put(method, method.getDefaultValue()); continue; } String name = this.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 annotationClass) { String annotationClassName = annotationClass.getCanonicalName(); for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { String name = this.typeHelper.getType(annotationMirror); if (name.contentEquals(annotationClassName)) { return annotationMirror; } } return null; } }