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