001package ball.annotation.processing; 002/*- 003 * ########################################################################## 004 * Utilities 005 * $Id: AnnotatedNoAnnotationProcessor.java 7215 2021-01-03 18:39:51Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/annotation/processing/AnnotatedNoAnnotationProcessor.java $ 007 * %% 008 * Copyright (C) 2008 - 2021 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import java.lang.annotation.Annotation; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.EnumSet; 028import java.util.List; 029import java.util.Set; 030import java.util.function.Consumer; 031import java.util.function.Predicate; 032import javax.annotation.processing.ProcessingEnvironment; 033import javax.annotation.processing.RoundEnvironment; 034import javax.lang.model.element.Element; 035import javax.lang.model.element.ElementKind; 036import javax.lang.model.element.Modifier; 037import javax.lang.model.element.TypeElement; 038import lombok.NoArgsConstructor; 039import lombok.ToString; 040 041import static ball.util.Walker.walk; 042import static java.util.Collections.disjoint; 043import static javax.lang.model.element.Modifier.ABSTRACT; 044import static javax.tools.Diagnostic.Kind.ERROR; 045import static javax.tools.Diagnostic.Kind.WARNING; 046import static lombok.AccessLevel.PROTECTED; 047 048/** 049 * Abstract {@link javax.annotation.processing.Processor} base class for 050 * processing "no" {@link java.lang.annotation.Annotation} ({@code "*"}). 051 * 052 * {@bean.info} 053 * 054 * @see ForElementKinds 055 * @see ForSubclassesOf 056 * @see MustImplement 057 * 058 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 059 * @version $Revision: 7215 $ 060 */ 061@NoArgsConstructor(access = PROTECTED) @ToString 062public abstract class AnnotatedNoAnnotationProcessor extends AbstractProcessor { 063 protected final List<Predicate<Element>> criteria = new ArrayList<>(); 064 protected final List<Consumer<Element>> checks = new ArrayList<>(); 065 066 /** 067 * See {@link ForElementKinds}. 068 * 069 * @return The {@link EnumSet} of {@link ElementKind}s specified by the 070 * annotation ({@code null} if no annotation present). 071 */ 072 protected EnumSet<ElementKind> getForElementKinds() { 073 EnumSet<ElementKind> value = null; 074 075 if (getClass().isAnnotationPresent(ForElementKinds.class)) { 076 ElementKind[] array = 077 getClass().getAnnotation(ForElementKinds.class).value(); 078 079 value = toEnumSet(array); 080 } 081 082 return value; 083 } 084 085 /** 086 * See {@link WithModifiers}. 087 * 088 * @return The {@link EnumSet} of {@link Modifier}s specified by the 089 * annotation ({@code null} if no annotation present). 090 */ 091 protected EnumSet<Modifier> getWithModifiers() { 092 EnumSet<Modifier> value = null; 093 094 if (getClass().isAnnotationPresent(WithModifiers.class)) { 095 Modifier[] array = 096 getClass().getAnnotation(WithModifiers.class).value(); 097 098 value = toEnumSet(array); 099 } 100 101 return value; 102 } 103 104 /** 105 * See {@link WithoutModifiers}. 106 * 107 * @return The {@link EnumSet} of {@link Modifier}s specified by the 108 * annotation ({@code null} if no annotation present). 109 */ 110 protected EnumSet<Modifier> getWithoutModifiers() { 111 EnumSet<Modifier> value = null; 112 113 if (getClass().isAnnotationPresent(WithoutModifiers.class)) { 114 Modifier[] array = 115 getClass().getAnnotation(WithoutModifiers.class).value(); 116 117 value = toEnumSet(array); 118 } 119 120 return value; 121 } 122 123 /** 124 * See {@link ForSubclassesOf}. 125 * 126 * @return The {@link Class} specified by the annotation ({@code null} 127 * if no annotation present). 128 */ 129 protected Class<?> getForSubclassesOf() { 130 Class<?> value = null; 131 132 if (getClass().isAnnotationPresent(ForSubclassesOf.class)) { 133 value = getClass().getAnnotation(ForSubclassesOf.class).value(); 134 } 135 136 return value; 137 } 138 139 /** 140 * See {@link MustImplement}. 141 * 142 * @return The array of {@link Class}es specified by the annotation 143 * ({@code null} if no annotation present). 144 */ 145 protected Class<?>[] getMustImplement() { 146 Class<?>[] value = null; 147 148 if (getClass().isAnnotationPresent(MustImplement.class)) { 149 value = getClass().getAnnotation(MustImplement.class).value(); 150 } 151 152 return value; 153 } 154 155 @Override 156 public Set<String> getSupportedAnnotationTypes() { 157 return Collections.singleton("*"); 158 } 159 160 @Override 161 public void init(ProcessingEnvironment processingEnv) { 162 super.init(processingEnv); 163 164 try { 165 EnumSet<ElementKind> kinds = EnumSet.allOf(ElementKind.class); 166 167 criteria.add(t -> kinds.contains(t.getKind())); 168 169 if (getClass().isAnnotationPresent(ForElementKinds.class)) { 170 kinds.retainAll(getForElementKinds()); 171 } 172 173 if (getClass().isAnnotationPresent(WithModifiers.class)) { 174 criteria.add(withModifiers(getWithModifiers())); 175 } 176 177 if (getClass().isAnnotationPresent(WithoutModifiers.class)) { 178 criteria.add(withoutModifiers(getWithoutModifiers())); 179 } 180 181 if (getClass().isAnnotationPresent(ForSubclassesOf.class)) { 182 kinds.retainAll(ForSubclassesOf.ELEMENT_KINDS); 183 criteria.add(isAssignableTo(getForSubclassesOf())); 184 } 185 186 if (getClass().isAnnotationPresent(MustImplement.class)) { 187 criteria.add(new MustImplementCriterion()); 188 } 189 } catch (Exception exception) { 190 criteria.clear(); 191 criteria.add(t -> false); 192 193 print(WARNING, "%s disabled", getClass().getName()); 194 /* print(WARNING, exception); */ 195 } 196 } 197 198 /** 199 * @return {@code false} always. 200 */ 201 @Override 202 public boolean process(Set<? extends TypeElement> annotations, 203 RoundEnvironment roundEnv) { 204 try { 205 walk(roundEnv.getRootElements(), Element::getEnclosedElements) 206 .filter(criteria.stream().reduce(t -> true, Predicate::and)) 207 .peek(checks.stream().reduce(t -> {}, Consumer::andThen)) 208 .forEach(t -> process(roundEnv, t)); 209 } catch (Throwable throwable) { 210 print(ERROR, throwable); 211 } 212 213 return false; 214 } 215 216 /** 217 * Method to process each {@link Element}. Default implementation does 218 * nothing. 219 * 220 * @param roundEnv The {@link RoundEnvironment}. 221 * @param element The {@link Element}. 222 */ 223 protected void process(RoundEnvironment roundEnv, Element element) { 224 } 225 226 @NoArgsConstructor @ToString 227 private class MustImplementCriterion extends Criterion<Element> { 228 private final Class<?>[] types = getMustImplement(); 229 230 @Override 231 public boolean test(Element element) { 232 boolean match = withoutModifiers(ABSTRACT).test(element); 233 234 if (match) { 235 for (Class<?> type : types) { 236 if (! isAssignableTo(type).test(element)) { 237 match &= false; 238 239 print(ERROR, element, 240 "@%s: @%s does not implement %s", 241 MustImplement.class.getSimpleName(), 242 element.getKind(), type.getName()); 243 } 244 } 245 } 246 247 return match; 248 } 249 } 250}