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