001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.index.processor; 018 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.EnumSet; 023import java.util.LinkedHashSet; 024import java.util.List; 025import java.util.Set; 026 027import javax.annotation.processing.Completion; 028import javax.annotation.processing.ProcessingEnvironment; 029import javax.annotation.processing.Processor; 030import javax.annotation.processing.RoundEnvironment; 031import javax.lang.model.SourceVersion; 032import javax.lang.model.element.AnnotationMirror; 033import javax.lang.model.element.Element; 034import javax.lang.model.element.ElementKind; 035import javax.lang.model.element.ExecutableElement; 036import javax.lang.model.element.Modifier; 037import javax.lang.model.element.TypeElement; 038 039/** 040 * Annotation {@link Processor} that writes {@link CandidateComponentsMetadata} 041 * file for spring components. 042 * 043 * @author Stephane Nicoll 044 * @author Juergen Hoeller 045 * @since 5.0 046 */ 047public class CandidateComponentsIndexer implements Processor { 048 049 private static final Set<ElementKind> TYPE_KINDS = 050 Collections.unmodifiableSet(EnumSet.of(ElementKind.CLASS, ElementKind.INTERFACE)); 051 052 private MetadataStore metadataStore; 053 054 private MetadataCollector metadataCollector; 055 056 private TypeHelper typeHelper; 057 058 private List<StereotypesProvider> stereotypesProviders; 059 060 061 @Override 062 public Set<String> getSupportedOptions() { 063 return Collections.emptySet(); 064 } 065 066 @Override 067 public Set<String> getSupportedAnnotationTypes() { 068 return Collections.singleton("*"); 069 } 070 071 @Override 072 public SourceVersion getSupportedSourceVersion() { 073 return SourceVersion.latest(); 074 } 075 076 @Override 077 public synchronized void init(ProcessingEnvironment env) { 078 this.stereotypesProviders = getStereotypesProviders(env); 079 this.typeHelper = new TypeHelper(env); 080 this.metadataStore = new MetadataStore(env); 081 this.metadataCollector = new MetadataCollector(env, this.metadataStore.readMetadata()); 082 } 083 084 @Override 085 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 086 this.metadataCollector.processing(roundEnv); 087 roundEnv.getRootElements().forEach(this::processElement); 088 if (roundEnv.processingOver()) { 089 writeMetaData(); 090 } 091 return false; 092 } 093 094 @Override 095 public Iterable<? extends Completion> getCompletions( 096 Element element, AnnotationMirror annotation, ExecutableElement member, String userText) { 097 098 return Collections.emptyList(); 099 } 100 101 102 private List<StereotypesProvider> getStereotypesProviders(ProcessingEnvironment env) { 103 List<StereotypesProvider> result = new ArrayList<>(); 104 TypeHelper typeHelper = new TypeHelper(env); 105 result.add(new IndexedStereotypesProvider(typeHelper)); 106 result.add(new StandardStereotypesProvider(typeHelper)); 107 result.add(new PackageInfoStereotypesProvider()); 108 return result; 109 } 110 111 private void processElement(Element element) { 112 addMetadataFor(element); 113 staticTypesIn(element.getEnclosedElements()).forEach(this::processElement); 114 } 115 116 private void addMetadataFor(Element element) { 117 Set<String> stereotypes = new LinkedHashSet<>(); 118 this.stereotypesProviders.forEach(p -> stereotypes.addAll(p.getStereotypes(element))); 119 if (!stereotypes.isEmpty()) { 120 this.metadataCollector.add(new ItemMetadata(this.typeHelper.getType(element), stereotypes)); 121 } 122 } 123 124 private void writeMetaData() { 125 CandidateComponentsMetadata metadata = this.metadataCollector.getMetadata(); 126 if (!metadata.getItems().isEmpty()) { 127 try { 128 this.metadataStore.writeMetadata(metadata); 129 } 130 catch (IOException ex) { 131 throw new IllegalStateException("Failed to write metadata", ex); 132 } 133 } 134 } 135 136 private static List<TypeElement> staticTypesIn(Iterable<? extends Element> elements) { 137 List<TypeElement> list = new ArrayList<>(); 138 for (Element element : elements) { 139 if (TYPE_KINDS.contains(element.getKind()) && element.getModifiers().contains(Modifier.STATIC)) { 140 list.add(TypeElement.class.cast(element)); 141 } 142 } 143 return list; 144 } 145 146}