001package ball.annotation.processing; 002/*- 003 * ########################################################################## 004 * Utilities 005 * $Id: AnnotatedNoAnnotationProcessor.java 6126 2020-06-06 15:11:56Z 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 - 2020 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 * @see ForElementKinds 053 * @see ForSubclassesOf 054 * @see MustImplement 055 * 056 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 057 * @version $Revision: 6126 $ 058 */ 059@NoArgsConstructor(access = PROTECTED) @ToString 060public abstract class AnnotatedNoAnnotationProcessor extends AbstractProcessor { 061 protected final List<Predicate<Element>> criteria = new ArrayList<>(); 062 protected final List<Consumer<Element>> checks = new ArrayList<>(); 063 064 /** 065 * See {@link ForElementKinds}. 066 * 067 * @return The {@link EnumSet} of {@link ElementKind}s specified by the 068 * annotation ({@code null} if no annotation present). 069 */ 070 protected EnumSet<ElementKind> getForElementKinds() { 071 EnumSet<ElementKind> value = null; 072 073 if (getClass().isAnnotationPresent(ForElementKinds.class)) { 074 ElementKind[] array = 075 getClass().getAnnotation(ForElementKinds.class).value(); 076 077 value = toEnumSet(array); 078 } 079 080 return value; 081 } 082 083 /** 084 * See {@link WithModifiers}. 085 * 086 * @return The {@link EnumSet} of {@link Modifier}s specified by the 087 * annotation ({@code null} if no annotation present). 088 */ 089 protected EnumSet<Modifier> getWithModifiers() { 090 EnumSet<Modifier> value = null; 091 092 if (getClass().isAnnotationPresent(WithModifiers.class)) { 093 Modifier[] array = 094 getClass().getAnnotation(WithModifiers.class).value(); 095 096 value = toEnumSet(array); 097 } 098 099 return value; 100 } 101 102 /** 103 * See {@link WithoutModifiers}. 104 * 105 * @return The {@link EnumSet} of {@link Modifier}s specified by the 106 * annotation ({@code null} if no annotation present). 107 */ 108 protected EnumSet<Modifier> getWithoutModifiers() { 109 EnumSet<Modifier> value = null; 110 111 if (getClass().isAnnotationPresent(WithoutModifiers.class)) { 112 Modifier[] array = 113 getClass().getAnnotation(WithoutModifiers.class).value(); 114 115 value = toEnumSet(array); 116 } 117 118 return value; 119 } 120 121 /** 122 * See {@link ForSubclassesOf}. 123 * 124 * @return The {@link Class} specified by the annotation ({@code null} 125 * if no annotation present). 126 */ 127 protected Class<?> getForSubclassesOf() { 128 Class<?> value = null; 129 130 if (getClass().isAnnotationPresent(ForSubclassesOf.class)) { 131 value = getClass().getAnnotation(ForSubclassesOf.class).value(); 132 } 133 134 return value; 135 } 136 137 /** 138 * See {@link MustImplement}. 139 * 140 * @return The array of {@link Class}es specified by the annotation 141 * ({@code null} if no annotation present). 142 */ 143 protected Class<?>[] getMustImplement() { 144 Class<?>[] value = null; 145 146 if (getClass().isAnnotationPresent(MustImplement.class)) { 147 value = getClass().getAnnotation(MustImplement.class).value(); 148 } 149 150 return value; 151 } 152 153 @Override 154 public Set<String> getSupportedAnnotationTypes() { 155 return Collections.singleton("*"); 156 } 157 158 @Override 159 public void init(ProcessingEnvironment processingEnv) { 160 super.init(processingEnv); 161 162 try { 163 EnumSet<ElementKind> kinds = EnumSet.allOf(ElementKind.class); 164 165 criteria.add(t -> kinds.contains(t.getKind())); 166 167 if (getClass().isAnnotationPresent(ForElementKinds.class)) { 168 kinds.retainAll(getForElementKinds()); 169 } 170 171 if (getClass().isAnnotationPresent(WithModifiers.class)) { 172 criteria.add(withModifiers(getWithModifiers())); 173 } 174 175 if (getClass().isAnnotationPresent(WithoutModifiers.class)) { 176 criteria.add(withoutModifiers(getWithoutModifiers())); 177 } 178 179 if (getClass().isAnnotationPresent(ForSubclassesOf.class)) { 180 kinds.retainAll(ForSubclassesOf.ELEMENT_KINDS); 181 criteria.add(isAssignableTo(getForSubclassesOf())); 182 } 183 184 if (getClass().isAnnotationPresent(MustImplement.class)) { 185 criteria.add(new MustImplementCriterion()); 186 } 187 } catch (Exception exception) { 188 criteria.clear(); 189 criteria.add(t -> false); 190 191 print(WARNING, "%s disabled", getClass().getName()); 192 /* print(WARNING, exception); */ 193 } 194 } 195 196 /** 197 * @return {@code false} always. 198 */ 199 @Override 200 public boolean process(Set<? extends TypeElement> annotations, 201 RoundEnvironment roundEnv) { 202 try { 203 walk(roundEnv.getRootElements(), Element::getEnclosedElements) 204 .filter(criteria.stream().reduce(t -> true, Predicate::and)) 205 .peek(checks.stream().reduce(t -> {}, Consumer::andThen)) 206 .forEach(t -> process(roundEnv, t)); 207 } catch (Throwable throwable) { 208 print(ERROR, throwable); 209 } 210 211 return false; 212 } 213 214 /** 215 * Method to process each {@link Element}. Default implementation does 216 * nothing. 217 * 218 * @param roundEnv The {@link RoundEnvironment}. 219 * @param element The {@link Element}. 220 */ 221 protected void process(RoundEnvironment roundEnv, Element element) { 222 } 223 224 @NoArgsConstructor @ToString 225 private class MustImplementCriterion extends Criterion<Element> { 226 private final Class<?>[] types = getMustImplement(); 227 228 @Override 229 public boolean test(Element element) { 230 boolean match = withoutModifiers(ABSTRACT).test(element); 231 232 if (match) { 233 for (Class<?> type : types) { 234 if (! isAssignableTo(type).test(element)) { 235 match &= false; 236 237 print(ERROR, element, 238 "@%s: @%s does not implement %s", 239 MustImplement.class.getSimpleName(), 240 element.getKind(), type.getName()); 241 } 242 } 243 } 244 245 return match; 246 } 247 } 248}