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 javax.lang.model.element.Modifier.ABSTRACT;
22 import static javax.lang.model.element.Modifier.PUBLIC;
23 import static javax.tools.Diagnostic.Kind.ERROR;
24 
25 import com.google.auto.common.MoreElements;
26 import com.google.auto.factory.AutoFactory;
27 import com.google.auto.factory.Provided;
28 import com.google.common.base.Function;
29 import com.google.common.base.Functions;
30 import com.google.common.base.Optional;
31 import com.google.common.base.Predicate;
32 import com.google.common.collect.FluentIterable;
33 import com.google.common.collect.ImmutableListMultimap;
34 import com.google.common.collect.ImmutableSet;
35 import com.google.common.collect.Multimaps;
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,
58       Types types,
59       AutoFactoryDeclaration.Factory declarationFactory) {
60     this.messager = messager;
61     this.types = types;
62     this.declarationFactory = declarationFactory;
63   }
64 
generateDescriptor(Element element)65   ImmutableSet<FactoryMethodDescriptor> generateDescriptor(Element element) {
66     final AnnotationMirror mirror = Mirrors.getAnnotationMirror(element, AutoFactory.class).get();
67     final Optional<AutoFactoryDeclaration> declaration = declarationFactory.createIfValid(element);
68     if (!declaration.isPresent()) {
69       return ImmutableSet.of();
70     }
71     return element.accept(new ElementKindVisitor6<ImmutableSet<FactoryMethodDescriptor>, Void>() {
72       @Override
73       protected ImmutableSet<FactoryMethodDescriptor> defaultAction(Element e, Void p) {
74         throw new AssertionError("@AutoFactory applied to an impossible element");
75       }
76 
77       @Override
78       public ImmutableSet<FactoryMethodDescriptor> visitTypeAsClass(TypeElement type, Void p) {
79         if (type.getModifiers().contains(ABSTRACT)) {
80           // applied to an abstract factory
81           messager.printMessage(ERROR,
82               "Auto-factory doesn't support being applied to abstract classes.", type, mirror);
83           return ImmutableSet.of();
84         } else {
85           // applied to the type to be created
86           ImmutableSet<ExecutableElement> constructors = Elements2.getConstructors(type);
87           if (constructors.isEmpty()) {
88             return generateDescriptorForDefaultConstructor(declaration.get(), type);
89           } else {
90             return FluentIterable.from(constructors)
91                 .transform(new Function<ExecutableElement, FactoryMethodDescriptor>() {
92                   @Override public FactoryMethodDescriptor apply(ExecutableElement constructor) {
93                     return generateDescriptorForConstructor(declaration.get(), constructor);
94                   }
95                 })
96                 .toSet();
97           }
98         }
99       }
100 
101       @Override
102       public ImmutableSet<FactoryMethodDescriptor> visitTypeAsInterface(TypeElement type, Void p) {
103         // applied to the factory interface
104         messager.printMessage(ERROR,
105             "Auto-factory doesn't support being applied to interfaces.", type, mirror);
106         return ImmutableSet.of();
107       }
108 
109       @Override
110       public ImmutableSet<FactoryMethodDescriptor> visitExecutableAsConstructor(ExecutableElement e,
111           Void p) {
112         // applied to a constructor of a type to be created
113         return ImmutableSet.of(generateDescriptorForConstructor(declaration.get(), e));
114       }
115     }, null);
116   }
117 
118   FactoryMethodDescriptor generateDescriptorForConstructor(final AutoFactoryDeclaration declaration,
119       ExecutableElement constructor) {
120     checkNotNull(constructor);
121     checkArgument(constructor.getKind() == ElementKind.CONSTRUCTOR);
122     TypeElement classElement = MoreElements.asType(constructor.getEnclosingElement());
123     ImmutableListMultimap<Boolean, ? extends VariableElement> parameterMap =
124         Multimaps.index(constructor.getParameters(), Functions.forPredicate(
125             new Predicate<VariableElement>() {
126               @Override
127               public boolean apply(VariableElement parameter) {
128                 return isAnnotationPresent(parameter, Provided.class);
129               }
130             }));
131     ImmutableSet<Parameter> providedParameters =
132         Parameter.forParameterList(parameterMap.get(true), types);
133     ImmutableSet<Parameter> passedParameters =
134         Parameter.forParameterList(parameterMap.get(false), types);
135     return FactoryMethodDescriptor.builder(declaration)
136         .name("create")
137         .returnType(classElement.asType())
138         .publicMethod(classElement.getModifiers().contains(PUBLIC))
139         .providedParameters(providedParameters)
140         .passedParameters(passedParameters)
141         .creationParameters(Parameter.forParameterList(constructor.getParameters(), types))
142         .isVarArgs(constructor.isVarArgs())
143         .build();
144   }
145 
146   private ImmutableSet<FactoryMethodDescriptor> generateDescriptorForDefaultConstructor(
147       AutoFactoryDeclaration declaration, TypeElement type) {
148     return ImmutableSet.of(
149         FactoryMethodDescriptor.builder(declaration)
150             .name("create")
151             .returnType(type.asType())
152             .publicMethod(type.getModifiers().contains(PUBLIC))
153             .passedParameters(ImmutableSet.<Parameter>of())
154             .creationParameters(ImmutableSet.<Parameter>of())
155             .providedParameters(ImmutableSet.<Parameter>of())
156             .build());
157   }
158 }
159