1 /* 2 * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.annotation.processing; 27 28 import java.util.Set; 29 import java.util.HashSet; 30 import java.util.Collections; 31 import java.util.Objects; 32 import javax.lang.model.element.*; 33 import javax.lang.model.SourceVersion; 34 import javax.tools.Diagnostic; 35 36 /** 37 * An abstract annotation processor designed to be a convenient 38 * superclass for most concrete annotation processors. This class 39 * examines annotation values to compute the {@linkplain 40 * #getSupportedOptions options}, {@linkplain 41 * #getSupportedAnnotationTypes annotation types}, and {@linkplain 42 * #getSupportedSourceVersion source version} supported by its 43 * subtypes. 44 * 45 * <p>The getter methods may {@linkplain Messager#printMessage issue 46 * warnings} about noteworthy conditions using the facilities available 47 * after the processor has been {@linkplain #isInitialized 48 * initialized}. 49 * 50 * <p>Subclasses are free to override the implementation and 51 * specification of any of the methods in this class as long as the 52 * general {@link javax.annotation.processing.Processor Processor} 53 * contract for that method is obeyed. 54 * 55 * @author Joseph D. Darcy 56 * @author Scott Seligman 57 * @author Peter von der Ahé 58 * @since 1.6 59 */ 60 public abstract class AbstractProcessor implements Processor { 61 /** 62 * Processing environment providing by the tool framework. 63 */ 64 protected ProcessingEnvironment processingEnv; 65 private boolean initialized = false; 66 67 /** 68 * Constructor for subclasses to call. 69 */ 70 protected AbstractProcessor() {} 71 72 /** 73 * If the processor class is annotated with {@link 74 * SupportedOptions}, return an unmodifiable set with the same set 75 * of strings as the annotation. If the class is not so 76 * annotated, an empty set is returned. 77 * 78 * @return the options recognized by this processor, or an empty 79 * set if none 80 */ 81 public Set<String> getSupportedOptions() { 82 SupportedOptions so = this.getClass().getAnnotation(SupportedOptions.class); 83 if (so == null) 84 return Collections.emptySet(); 85 else 86 return arrayToSet(so.value(), false); 87 } 88 89 /** 90 * If the processor class is annotated with {@link 91 * SupportedAnnotationTypes}, return an unmodifiable set with the 92 * same set of strings as the annotation. If the class is not so 93 * annotated, an empty set is returned. 94 * 95 * If the {@linkplain ProcessingEnvironment#getSourceVersion source 96 * version} does not support modules, in other words if it is less 97 * than or equal to {@link SourceVersion#RELEASE_8 RELEASE_8}, 98 * then any leading {@linkplain Processor#getSupportedAnnotationTypes 99 * module prefixes} are stripped from the names. 100 * 101 * @return the names of the annotation types supported by this 102 * processor, or an empty set if none 103 */ 104 public Set<String> getSupportedAnnotationTypes() { 105 SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class); 106 boolean initialized = isInitialized(); 107 if (sat == null) { 108 if (initialized) 109 processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, 110 "No SupportedAnnotationTypes annotation " + 111 "found on " + this.getClass().getName() + 112 ", returning an empty set."); 113 return Collections.emptySet(); 114 } else { 115 boolean stripModulePrefixes = 116 initialized && 117 processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_8) <= 0; 118 return arrayToSet(sat.value(), stripModulePrefixes); 119 } 120 } 121 122 /** 123 * If the processor class is annotated with {@link 124 * SupportedSourceVersion}, return the source version in the 125 * annotation. If the class is not so annotated, {@link 126 * SourceVersion#RELEASE_6} is returned. 127 * 128 * @return the latest source version supported by this processor 129 */ 130 public SourceVersion getSupportedSourceVersion() { 131 SupportedSourceVersion ssv = this.getClass().getAnnotation(SupportedSourceVersion.class); 132 SourceVersion sv = null; 133 if (ssv == null) { 134 sv = SourceVersion.RELEASE_6; 135 if (isInitialized()) 136 processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, 137 "No SupportedSourceVersion annotation " + 138 "found on " + this.getClass().getName() + 139 ", returning " + sv + "."); 140 } else 141 sv = ssv.value(); 142 return sv; 143 } 144 145 146 /** 147 * Initializes the processor with the processing environment by 148 * setting the {@code processingEnv} field to the value of the 149 * {@code processingEnv} argument. An {@code 150 * IllegalStateException} will be thrown if this method is called 151 * more than once on the same object. 152 * 153 * @param processingEnv environment to access facilities the tool framework 154 * provides to the processor 155 * @throws IllegalStateException if this method is called more than once. 156 */ 157 public synchronized void init(ProcessingEnvironment processingEnv) { 158 if (initialized) 159 throw new IllegalStateException("Cannot call init more than once."); 160 Objects.requireNonNull(processingEnv, "Tool provided null ProcessingEnvironment"); 161 162 this.processingEnv = processingEnv; 163 initialized = true; 164 } 165 166 /** 167 * {@inheritDoc} 168 */ 169 public abstract boolean process(Set<? extends TypeElement> annotations, 170 RoundEnvironment roundEnv); 171 172 /** 173 * Returns an empty iterable of completions. 174 * 175 * @param element {@inheritDoc} 176 * @param annotation {@inheritDoc} 177 * @param member {@inheritDoc} 178 * @param userText {@inheritDoc} 179 */ 180 public Iterable<? extends Completion> getCompletions(Element element, 181 AnnotationMirror annotation, 182 ExecutableElement member, 183 String userText) { 184 return Collections.emptyList(); 185 } 186 187 /** 188 * Returns {@code true} if this object has been {@linkplain #init 189 * initialized}, {@code false} otherwise. 190 * 191 * @return {@code true} if this object has been initialized, 192 * {@code false} otherwise. 193 */ 194 protected synchronized boolean isInitialized() { 195 return initialized; 196 } 197 198 private static Set<String> arrayToSet(String[] array, 199 boolean stripModulePrefixes) { 200 assert array != null; 201 Set<String> set = new HashSet<>(array.length); 202 for (String s : array) { 203 if (stripModulePrefixes) { 204 int index = s.indexOf('/'); 205 if (index != -1) 206 s = s.substring(index + 1); 207 } 208 set.add(s); 209 } 210 return Collections.unmodifiableSet(set); 211 } 212 }