/** * Copyright © 2006-2016 Web Cohesion (info@webcohesion.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.webcohesion.enunciate; import com.webcohesion.enunciate.javac.decorations.*; import com.webcohesion.enunciate.module.ContextModifyingModule; import com.webcohesion.enunciate.module.EnunciateModule; import org.jgrapht.DirectedGraph; import org.jgrapht.graph.DefaultEdge; import rx.Observable; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import java.util.*; import static com.webcohesion.enunciate.util.IgnoreUtils.isIgnored; /** * @author Ryan Heaton */ @SupportedOptions({}) @SupportedAnnotationTypes("*") public class EnunciateAnnotationProcessor extends AbstractProcessor { private final Enunciate enunciate; private final Set<String> includedTypes; private EnunciateContext context; protected boolean processed = false; public EnunciateAnnotationProcessor(Enunciate enunciate, Set<String> includedTypes) { this.enunciate = enunciate; this.includedTypes = includedTypes; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); //set up the processing environment. ArrayList<ElementDecoration> elementDecorations = new ArrayList<ElementDecoration>(); ArrayList<TypeMirrorDecoration> typeMirrorDecorations = new ArrayList<TypeMirrorDecoration>(); ArrayList<AnnotationMirrorDecoration> annotationMirrorDecorations = new ArrayList<AnnotationMirrorDecoration>(); DecoratedProcessingEnvironment processingEnvironment = new DecoratedProcessingEnvironment(processingEnv, elementDecorations, typeMirrorDecorations, annotationMirrorDecorations); //construct a context. this.context = new EnunciateContext(processingEnvironment, this.enunciate.getLogger(), this.enunciate.getApiRegistry(), this.enunciate.getConfiguration(), this.enunciate.getIncludePatterns(), this.enunciate.getExcludePatterns()); //initialize the modules. for (EnunciateModule module : this.enunciate.getModules()) { module.init(this.context); if (module instanceof ContextModifyingModule) { ContextModifyingModule contextModifier = (ContextModifyingModule) module; elementDecorations.addAll(contextModifier.getElementDecorations()); typeMirrorDecorations.addAll(contextModifier.getTypeMirrorDecorations()); annotationMirrorDecorations.addAll(contextModifier.getAnnotationMirrorDecorations()); } } } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { // (heatonra) I still don't understand why this check is needed. But if I don't do the check, the processing happens twice. //find all the processing elements and set them on the context. Set<Element> apiElements = new HashSet<Element>(); Set<Element> localApiElements = new HashSet<Element>(); for (Element element : roundEnv.getRootElements()) { Element el = ElementDecorator.decorate(element, this.context.getProcessingEnvironment()); apiElements.add(el); localApiElements.add(el); } Elements elementUtils = this.context.getProcessingEnvironment().getElementUtils(); for (String includedType : this.includedTypes) { TypeElement typeElement = elementUtils.getTypeElement(includedType); if (typeElement != null) { apiElements.add(typeElement); } else { this.enunciate.getLogger().debug("Unable to load type element %s.", includedType); } } applyExcludeFilter(apiElements); applyIncludeFilter(apiElements); this.enunciate.getLogger().debug("API Elements: %s", new EnunciateLogger.ListWriter(apiElements)); applyExcludeFilter(localApiElements); this.enunciate.getLogger().debug("Local API Elements: %s", new EnunciateLogger.ListWriter(localApiElements)); this.context.setRoundEnvironment(new DecoratedRoundEnvironment(roundEnv, this.context.getProcessingEnvironment())); this.context.setLocalApiElements(localApiElements); this.context.setApiElements(apiElements); //compose the engine. Map<String, ? extends EnunciateModule> enabledModules = this.enunciate.findEnabledModules(); DirectedGraph<String, DefaultEdge> graph = this.enunciate.buildModuleGraph(enabledModules); Observable<EnunciateContext> engine = this.enunciate.composeEngine(this.context, enabledModules, graph); //fire off (and block on) the engine. engine.toList().toBlocking().single(); this.processed = true; } return false; //always return 'false' in case other annotation processors want to continue. } protected void applyExcludeFilter(Set<Element> apiElements) { Iterator<Element> elementIterator = apiElements.iterator(); while (elementIterator.hasNext()) { Element next = elementIterator.next(); if (this.context.isExcluded(next) || isIgnored(next)) { elementIterator.remove(); } } } protected void applyIncludeFilter(Set<Element> apiElements) { if (this.context.hasExplicitIncludes()) { Iterator<Element> elementIterator = apiElements.iterator(); while (elementIterator.hasNext()) { Element next = elementIterator.next(); if (!this.context.isExplicitlyIncluded(next)) { elementIterator.remove(); } } } } }