• 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.isAnnotationPresent;
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static java.util.Objects.requireNonNull;
22 import static java.util.stream.Collectors.partitioningBy;
23 import static javax.lang.model.element.Modifier.ABSTRACT;
24 import static javax.lang.model.element.Modifier.PUBLIC;
25 import static javax.tools.Diagnostic.Kind.ERROR;
26 
27 import com.google.auto.common.MoreElements;
28 import com.google.auto.factory.AutoFactory;
29 import com.google.auto.factory.Provided;
30 import com.google.common.base.Function;
31 import com.google.common.collect.FluentIterable;
32 import com.google.common.collect.ImmutableSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Optional;
36 import javax.annotation.processing.Messager;
37 import javax.lang.model.element.AnnotationMirror;
38 import javax.lang.model.element.Element;
39 import javax.lang.model.element.ElementKind;
40 import javax.lang.model.element.ExecutableElement;
41 import javax.lang.model.element.TypeElement;
42 import javax.lang.model.element.VariableElement;
43 import javax.lang.model.util.ElementKindVisitor6;
44 import javax.lang.model.util.Types;
45 
46 /**
47  * A service that traverses an element and returns the set of factory methods defined therein.
48  *
49  * @author Gregory Kick
50  */
51 final class FactoryDescriptorGenerator {
52   private final Messager messager;
53   private final Types types;
54   private final AutoFactoryDeclaration.Factory declarationFactory;
55 
FactoryDescriptorGenerator( Messager messager, Types types, AutoFactoryDeclaration.Factory declarationFactory)56   FactoryDescriptorGenerator(
57       Messager messager, Types types, AutoFactoryDeclaration.Factory declarationFactory) {
58     this.messager = messager;
59     this.types = types;
60     this.declarationFactory = declarationFactory;
61   }
62 
generateDescriptor(Element element)63   ImmutableSet<FactoryMethodDescriptor> generateDescriptor(Element element) {
64     final AnnotationMirror mirror = Mirrors.getAnnotationMirror(element, AutoFactory.class).get();
65     final Optional<AutoFactoryDeclaration> declaration = declarationFactory.createIfValid(element);
66     if (!declaration.isPresent()) {
67       return ImmutableSet.of();
68     }
69     return element.accept(
70         new ElementKindVisitor6<ImmutableSet<FactoryMethodDescriptor>, Void>() {
71           @Override
72           protected ImmutableSet<FactoryMethodDescriptor> defaultAction(Element e, Void p) {
73             throw new AssertionError("@AutoFactory applied to an impossible element");
74           }
75 
76           @Override
77           public ImmutableSet<FactoryMethodDescriptor> visitTypeAsClass(TypeElement type, Void p) {
78             if (type.getModifiers().contains(ABSTRACT)) {
79               // applied to an abstract factory
80               messager.printMessage(
81                   ERROR,
82                   "Auto-factory doesn't support being applied to abstract classes.",
83                   type,
84                   mirror);
85               return ImmutableSet.of();
86             } else {
87               // applied to the type to be created
88               ImmutableSet<ExecutableElement> constructors = Elements2.getConstructors(type);
89               if (constructors.isEmpty()) {
90                 return generateDescriptorForDefaultConstructor(declaration.get(), type);
91               } else {
92                 return FluentIterable.from(constructors)
93                     .transform(
94                         new Function<ExecutableElement, FactoryMethodDescriptor>() {
95                           @Override
96                           public FactoryMethodDescriptor apply(ExecutableElement constructor) {
97                             return generateDescriptorForConstructor(declaration.get(), constructor);
98                           }
99                         })
100                     .toSet();
101               }
102             }
103           }
104 
105           @Override
106           public ImmutableSet<FactoryMethodDescriptor> visitTypeAsInterface(
107               TypeElement type, Void p) {
108             // applied to the factory interface
109             messager.printMessage(
110                 ERROR, "Auto-factory doesn't support being applied to interfaces.", type, mirror);
111             return ImmutableSet.of();
112           }
113 
114           @Override
115           public ImmutableSet<FactoryMethodDescriptor> visitExecutableAsConstructor(
116               ExecutableElement e, Void p) {
117             // applied to a constructor of a type to be created
118             return ImmutableSet.of(generateDescriptorForConstructor(declaration.get(), e));
119           }
120         },
121         null);
122   }
123 
124   FactoryMethodDescriptor generateDescriptorForConstructor(
125       final AutoFactoryDeclaration declaration, ExecutableElement constructor) {
126     checkNotNull(constructor);
127     checkArgument(constructor.getKind() == ElementKind.CONSTRUCTOR);
128     TypeElement classElement = MoreElements.asType(constructor.getEnclosingElement());
129     Map<Boolean, List<VariableElement>> parameterMap =
130         constructor.getParameters().stream()
131             .collect(partitioningBy(parameter -> isAnnotationPresent(parameter, Provided.class)));
132     // The map returned by partitioningBy always has entries for both key values but our
133     // null-checker isn't yet smart enough to know that.
134     ImmutableSet<Parameter> providedParameters =
135         Parameter.forParameterList(requireNonNull(parameterMap.get(true)), types);
136     ImmutableSet<Parameter> passedParameters =
137         Parameter.forParameterList(requireNonNull(parameterMap.get(false)), types);
138     return FactoryMethodDescriptor.builder(declaration)
139         .name("create")
140         .returnType(classElement.asType())
141         .publicMethod(classElement.getModifiers().contains(PUBLIC))
142         .providedParameters(providedParameters)
143         .passedParameters(passedParameters)
144         .creationParameters(Parameter.forParameterList(constructor.getParameters(), types))
145         .isVarArgs(constructor.isVarArgs())
146         .exceptions(constructor.getThrownTypes())
147         .overridingMethod(false)
148         .build();
149   }
150 
151   private ImmutableSet<FactoryMethodDescriptor> generateDescriptorForDefaultConstructor(
152       AutoFactoryDeclaration declaration, TypeElement type) {
153     return ImmutableSet.of(
154         FactoryMethodDescriptor.builder(declaration)
155             .name("create")
156             .returnType(type.asType())
157             .publicMethod(type.getModifiers().contains(PUBLIC))
158             .providedParameters(ImmutableSet.of())
159             .passedParameters(ImmutableSet.of())
160             .creationParameters(ImmutableSet.of())
161             .isVarArgs(false)
162             .exceptions(ImmutableSet.of())
163             .overridingMethod(false)
164             .build());
165   }
166 }
167