• 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.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