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.factory.processor.Elements2.isValidSupertypeForClass; 20 import static com.google.common.base.Preconditions.checkArgument; 21 import static com.google.common.base.Preconditions.checkNotNull; 22 import static com.google.common.base.Preconditions.checkState; 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.factory.AutoFactory; 30 import com.google.auto.value.AutoValue; 31 import com.google.common.base.Predicate; 32 import com.google.common.collect.FluentIterable; 33 import com.google.common.collect.ImmutableList; 34 import com.google.common.collect.ImmutableMap; 35 import com.google.common.collect.ImmutableSet; 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Optional; 40 import javax.annotation.processing.Messager; 41 import javax.lang.model.SourceVersion; 42 import javax.lang.model.element.AnnotationMirror; 43 import javax.lang.model.element.AnnotationValue; 44 import javax.lang.model.element.Element; 45 import javax.lang.model.element.ExecutableElement; 46 import javax.lang.model.element.TypeElement; 47 import javax.lang.model.util.ElementFilter; 48 import javax.lang.model.util.Elements; 49 50 /** 51 * This is a value object that mirrors the static declaration of an {@link AutoFactory} annotation. 52 * 53 * @author Gregory Kick 54 */ 55 @AutoValue 56 abstract class AutoFactoryDeclaration { targetType()57 abstract TypeElement targetType(); 58 target()59 abstract Element target(); 60 className()61 abstract Optional<String> className(); 62 extendingType()63 abstract TypeElement extendingType(); 64 implementingTypes()65 abstract ImmutableSet<TypeElement> implementingTypes(); 66 allowSubclasses()67 abstract boolean allowSubclasses(); 68 mirror()69 abstract AnnotationMirror mirror(); 70 valuesMap()71 abstract ImmutableMap<String, AnnotationValue> valuesMap(); 72 getFactoryName()73 PackageAndClass getFactoryName() { 74 String packageName = getPackage(targetType()).getQualifiedName().toString(); 75 if (className().isPresent()) { 76 return PackageAndClass.of(packageName, className().get()); 77 } 78 StringBuilder builder = new StringBuilder(); 79 for (String enclosingSimpleName : targetEnclosingSimpleNames()) { 80 builder.append(enclosingSimpleName).append('_'); 81 } 82 builder.append(targetType().getSimpleName()).append("Factory"); 83 return PackageAndClass.of(packageName, builder.toString()); 84 } 85 targetEnclosingSimpleNames()86 private ImmutableList<String> targetEnclosingSimpleNames() { 87 ImmutableList.Builder<String> simpleNames = ImmutableList.builder(); 88 for (Element element = targetType().getEnclosingElement(); 89 !element.getKind().equals(PACKAGE); 90 element = element.getEnclosingElement()) { 91 simpleNames.add(element.getSimpleName().toString()); 92 } 93 return simpleNames.build().reverse(); 94 } 95 96 static final class Factory { 97 private final Elements elements; 98 private final Messager messager; 99 Factory(Elements elements, Messager messager)100 Factory(Elements elements, Messager messager) { 101 this.elements = elements; 102 this.messager = messager; 103 } 104 createIfValid(Element element)105 Optional<AutoFactoryDeclaration> createIfValid(Element element) { 106 checkNotNull(element); 107 AnnotationMirror mirror = Mirrors.getAnnotationMirror(element, AutoFactory.class).get(); 108 checkArgument( 109 Mirrors.getQualifiedName(mirror.getAnnotationType()) 110 .contentEquals(AutoFactory.class.getName())); 111 Map<String, AnnotationValue> values = 112 Mirrors.simplifyAnnotationValueMap(elements.getElementValuesWithDefaults(mirror)); 113 checkState(values.size() == 4); 114 115 // className value is a string, so we can just call toString. We know values.get("className") 116 // is non-null because @AutoFactory has an annotation element of that name. 117 AnnotationValue classNameValue = requireNonNull(values.get("className")); 118 String className = classNameValue.getValue().toString(); 119 if (!className.isEmpty() && !isValidIdentifier(className)) { 120 messager.printMessage( 121 ERROR, 122 String.format("\"%s\" is not a valid Java identifier", className), 123 element, 124 mirror, 125 classNameValue); 126 return Optional.empty(); 127 } 128 129 AnnotationValue extendingValue = checkNotNull(values.get("extending")); 130 TypeElement extendingType = AnnotationValues.asType(extendingValue); 131 if (extendingType == null) { 132 messager.printMessage( 133 ERROR, 134 "Unable to find the type: " + extendingValue.getValue(), 135 element, 136 mirror, 137 extendingValue); 138 return Optional.empty(); 139 } else if (!isValidSupertypeForClass(extendingType)) { 140 messager.printMessage( 141 ERROR, 142 String.format( 143 "%s is not a valid supertype for a factory. " 144 + "Supertypes must be non-final classes.", 145 extendingType.getQualifiedName()), 146 element, 147 mirror, 148 extendingValue); 149 return Optional.empty(); 150 } 151 ImmutableList<ExecutableElement> noParameterConstructors = 152 FluentIterable.from(ElementFilter.constructorsIn(extendingType.getEnclosedElements())) 153 .filter( 154 new Predicate<ExecutableElement>() { 155 @Override 156 public boolean apply(ExecutableElement constructor) { 157 return constructor.getParameters().isEmpty(); 158 } 159 }) 160 .toList(); 161 if (noParameterConstructors.isEmpty()) { 162 messager.printMessage( 163 ERROR, 164 String.format( 165 "%s is not a valid supertype for a factory. " 166 + "Factory supertypes must have a no-arg constructor.", 167 extendingType.getQualifiedName()), 168 element, 169 mirror, 170 extendingValue); 171 return Optional.empty(); 172 } else if (noParameterConstructors.size() > 1) { 173 throw new IllegalStateException("Multiple constructors with no parameters??"); 174 } 175 176 AnnotationValue implementingValue = checkNotNull(values.get("implementing")); 177 ImmutableSet.Builder<TypeElement> builder = ImmutableSet.builder(); 178 for (AnnotationValue implementingTypeValue : AnnotationValues.asList(implementingValue)) { 179 builder.add(AnnotationValues.asType(implementingTypeValue)); 180 } 181 ImmutableSet<TypeElement> implementingTypes = builder.build(); 182 183 AnnotationValue allowSubclassesValue = checkNotNull(values.get("allowSubclasses")); 184 boolean allowSubclasses = AnnotationValues.asBoolean(allowSubclassesValue); 185 186 return Optional.<AutoFactoryDeclaration>of( 187 new AutoValue_AutoFactoryDeclaration( 188 getAnnotatedType(element), 189 element, 190 className.isEmpty() ? Optional.empty() : Optional.of(className), 191 extendingType, 192 implementingTypes, 193 allowSubclasses, 194 mirror, 195 ImmutableMap.copyOf(values))); 196 } 197 getAnnotatedType(Element element)198 private static TypeElement getAnnotatedType(Element element) { 199 List<TypeElement> types = ImmutableList.of(); 200 while (types.isEmpty()) { 201 types = typesIn(Arrays.asList(element)); 202 element = element.getEnclosingElement(); 203 } 204 return getOnlyElement(types); 205 } 206 isValidIdentifier(String identifier)207 static boolean isValidIdentifier(String identifier) { 208 return SourceVersion.isIdentifier(identifier) && !SourceVersion.isKeyword(identifier); 209 } 210 } 211 } 212