1 /* 2 * Copyright 2013 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.auto.factory.processor; 17 18 import static com.google.auto.common.MoreElements.getPackage; 19 import static com.google.auto.common.MoreStreams.toImmutableSet; 20 import static com.google.auto.factory.processor.Elements2.isValidSupertypeForClass; 21 import static com.google.common.base.Preconditions.checkArgument; 22 import static com.google.common.base.Preconditions.checkNotNull; 23 import static com.google.common.collect.Iterables.getOnlyElement; 24 import static java.util.Objects.requireNonNull; 25 import static javax.lang.model.element.ElementKind.PACKAGE; 26 import static javax.lang.model.util.ElementFilter.typesIn; 27 import static javax.tools.Diagnostic.Kind.ERROR; 28 29 import com.google.auto.common.AnnotationMirrors; 30 import com.google.auto.factory.AutoFactory; 31 import com.google.auto.factory.AutoFactory.AnnotationsToApply; 32 import com.google.auto.value.AutoValue; 33 import com.google.common.base.Predicate; 34 import com.google.common.collect.FluentIterable; 35 import com.google.common.collect.ImmutableList; 36 import com.google.common.collect.ImmutableMap; 37 import com.google.common.collect.ImmutableSet; 38 import java.util.Arrays; 39 import java.util.Collection; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Optional; 43 import javax.annotation.processing.Messager; 44 import javax.lang.model.SourceVersion; 45 import javax.lang.model.element.AnnotationMirror; 46 import javax.lang.model.element.AnnotationValue; 47 import javax.lang.model.element.Element; 48 import javax.lang.model.element.ExecutableElement; 49 import javax.lang.model.element.TypeElement; 50 import javax.lang.model.util.ElementFilter; 51 import javax.lang.model.util.Elements; 52 53 /** 54 * This is a value object that mirrors the static declaration of an {@link AutoFactory} annotation. 55 * 56 * @author Gregory Kick 57 */ 58 @AutoValue 59 abstract class AutoFactoryDeclaration { targetType()60 abstract TypeElement targetType(); 61 target()62 abstract Element target(); 63 className()64 abstract Optional<String> className(); 65 annotations()66 abstract ImmutableSet<AnnotationMirror> annotations(); 67 extendingType()68 abstract TypeElement extendingType(); 69 implementingTypes()70 abstract ImmutableSet<TypeElement> implementingTypes(); 71 allowSubclasses()72 abstract boolean allowSubclasses(); 73 mirror()74 abstract AnnotationMirror mirror(); 75 valuesMap()76 abstract ImmutableMap<String, AnnotationValue> valuesMap(); 77 getFactoryName()78 PackageAndClass getFactoryName() { 79 String packageName = getPackage(targetType()).getQualifiedName().toString(); 80 if (className().isPresent()) { 81 return PackageAndClass.of(packageName, className().get()); 82 } 83 StringBuilder builder = new StringBuilder(); 84 for (String enclosingSimpleName : targetEnclosingSimpleNames()) { 85 builder.append(enclosingSimpleName).append('_'); 86 } 87 builder.append(targetType().getSimpleName()).append("Factory"); 88 return PackageAndClass.of(packageName, builder.toString()); 89 } 90 targetEnclosingSimpleNames()91 private ImmutableList<String> targetEnclosingSimpleNames() { 92 ImmutableList.Builder<String> simpleNames = ImmutableList.builder(); 93 for (Element element = targetType().getEnclosingElement(); 94 !element.getKind().equals(PACKAGE); 95 element = element.getEnclosingElement()) { 96 simpleNames.add(element.getSimpleName().toString()); 97 } 98 return simpleNames.build().reverse(); 99 } 100 101 static final class Factory { 102 private final Elements elements; 103 private final Messager messager; 104 Factory(Elements elements, Messager messager)105 Factory(Elements elements, Messager messager) { 106 this.elements = elements; 107 this.messager = messager; 108 } 109 createIfValid(Element element)110 Optional<AutoFactoryDeclaration> createIfValid(Element element) { 111 checkNotNull(element); 112 AnnotationMirror mirror = Mirrors.getAnnotationMirror(element, AutoFactory.class).get(); 113 checkArgument( 114 Mirrors.getQualifiedName(mirror.getAnnotationType()) 115 .contentEquals(AutoFactory.class.getName())); 116 Map<String, AnnotationValue> values = 117 Mirrors.simplifyAnnotationValueMap(elements.getElementValuesWithDefaults(mirror)); 118 119 // className value is a string, so we can just call toString. We know values.get("className") 120 // is non-null because @AutoFactory has an annotation element of that name. 121 AnnotationValue classNameValue = requireNonNull(values.get("className")); 122 String className = classNameValue.getValue().toString(); 123 if (!className.isEmpty() && !isValidIdentifier(className)) { 124 messager.printMessage( 125 ERROR, 126 String.format("\"%s\" is not a valid Java identifier", className), 127 element, 128 mirror, 129 classNameValue); 130 return Optional.empty(); 131 } 132 133 ImmutableSet<AnnotationMirror> annotations = annotationsToAdd(element); 134 AnnotationValue extendingValue = checkNotNull(values.get("extending")); 135 TypeElement extendingType = AnnotationValues.asType(extendingValue); 136 if (extendingType == null) { 137 messager.printMessage( 138 ERROR, 139 "Unable to find the type: " + extendingValue.getValue(), 140 element, 141 mirror, 142 extendingValue); 143 return Optional.empty(); 144 } else if (!isValidSupertypeForClass(extendingType)) { 145 messager.printMessage( 146 ERROR, 147 String.format( 148 "%s is not a valid supertype for a factory. " 149 + "Supertypes must be non-final classes.", 150 extendingType.getQualifiedName()), 151 element, 152 mirror, 153 extendingValue); 154 return Optional.empty(); 155 } 156 ImmutableList<ExecutableElement> noParameterConstructors = 157 FluentIterable.from(ElementFilter.constructorsIn(extendingType.getEnclosedElements())) 158 .filter( 159 new Predicate<ExecutableElement>() { 160 @Override 161 public boolean apply(ExecutableElement constructor) { 162 return constructor.getParameters().isEmpty(); 163 } 164 }) 165 .toList(); 166 if (noParameterConstructors.isEmpty()) { 167 messager.printMessage( 168 ERROR, 169 String.format( 170 "%s is not a valid supertype for a factory. " 171 + "Factory supertypes must have a no-arg constructor.", 172 extendingType.getQualifiedName()), 173 element, 174 mirror, 175 extendingValue); 176 return Optional.empty(); 177 } else if (noParameterConstructors.size() > 1) { 178 throw new IllegalStateException("Multiple constructors with no parameters??"); 179 } 180 181 AnnotationValue implementingValue = checkNotNull(values.get("implementing")); 182 ImmutableSet.Builder<TypeElement> builder = ImmutableSet.builder(); 183 for (AnnotationValue implementingTypeValue : AnnotationValues.asList(implementingValue)) { 184 builder.add(AnnotationValues.asType(implementingTypeValue)); 185 } 186 ImmutableSet<TypeElement> implementingTypes = builder.build(); 187 188 AnnotationValue allowSubclassesValue = checkNotNull(values.get("allowSubclasses")); 189 boolean allowSubclasses = AnnotationValues.asBoolean(allowSubclassesValue); 190 191 return Optional.<AutoFactoryDeclaration>of( 192 new AutoValue_AutoFactoryDeclaration( 193 getAnnotatedType(element), 194 element, 195 className.isEmpty() ? Optional.empty() : Optional.of(className), 196 annotations, 197 extendingType, 198 implementingTypes, 199 allowSubclasses, 200 mirror, 201 ImmutableMap.copyOf(values))); 202 } 203 getAnnotatedType(Element element)204 private static TypeElement getAnnotatedType(Element element) { 205 List<TypeElement> types = ImmutableList.of(); 206 while (types.isEmpty()) { 207 types = typesIn(Arrays.asList(element)); 208 element = element.getEnclosingElement(); 209 } 210 return getOnlyElement(types); 211 } 212 isValidIdentifier(String identifier)213 static boolean isValidIdentifier(String identifier) { 214 return SourceVersion.isIdentifier(identifier) && !SourceVersion.isKeyword(identifier); 215 } 216 annotationsToAdd(Element element)217 private ImmutableSet<AnnotationMirror> annotationsToAdd(Element element) { 218 ImmutableSet<? extends AnnotationMirror> containers = 219 AnnotationMirrors.getAnnotatedAnnotations(element, AnnotationsToApply.class); 220 if (containers.size() > 1) { 221 messager.printMessage( 222 ERROR, "Multiple @AnnotationsToApply annotations are not supported", element); 223 } 224 return containers.stream() 225 .limit(1) 226 .map(elements::getElementValuesWithDefaults) 227 .map(Map::values) 228 .flatMap(Collection::stream) 229 .map(AnnotationValue::getValue) 230 .filter(AnnotationMirror.class::isInstance) 231 // Any non-annotation element should already have been flagged when processing 232 // @AnnotationsToApply 233 .map(AnnotationMirror.class::cast) 234 .collect(toImmutableSet()); 235 } 236 } 237 } 238