• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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