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 javax.lang.model.element.ElementKind.PACKAGE; 25 import static javax.lang.model.util.ElementFilter.typesIn; 26 import static javax.tools.Diagnostic.Kind.ERROR; 27 28 import com.google.auto.factory.AutoFactory; 29 import com.google.auto.value.AutoValue; 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.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 javax.annotation.processing.Messager; 40 import javax.lang.model.SourceVersion; 41 import javax.lang.model.element.AnnotationMirror; 42 import javax.lang.model.element.AnnotationValue; 43 import javax.lang.model.element.Element; 44 import javax.lang.model.element.ExecutableElement; 45 import javax.lang.model.element.TypeElement; 46 import javax.lang.model.util.ElementFilter; 47 import javax.lang.model.util.Elements; 48 49 /** 50 * This is a value object that mirrors the static declaration of an {@link AutoFactory} annotation. 51 * 52 * @author Gregory Kick 53 */ 54 @AutoValue 55 abstract class AutoFactoryDeclaration { targetType()56 abstract TypeElement targetType(); target()57 abstract Element target(); className()58 abstract Optional<String> className(); extendingType()59 abstract TypeElement extendingType(); implementingTypes()60 abstract ImmutableSet<TypeElement> implementingTypes(); allowSubclasses()61 abstract boolean allowSubclasses(); mirror()62 abstract AnnotationMirror mirror(); valuesMap()63 abstract ImmutableMap<String, AnnotationValue> valuesMap(); 64 getFactoryName()65 PackageAndClass getFactoryName() { 66 String packageName = getPackage(targetType()).getQualifiedName().toString(); 67 if (className().isPresent()) { 68 return PackageAndClass.of(packageName, className().get()); 69 } 70 StringBuilder builder = new StringBuilder(); 71 for (String enclosingSimpleName : targetEnclosingSimpleNames()) { 72 builder.append(enclosingSimpleName).append('_'); 73 } 74 builder.append(targetType().getSimpleName()).append("Factory"); 75 return PackageAndClass.of(packageName, builder.toString()); 76 } 77 targetEnclosingSimpleNames()78 private ImmutableList<String> targetEnclosingSimpleNames() { 79 ImmutableList.Builder<String> simpleNames = ImmutableList.builder(); 80 for (Element element = targetType().getEnclosingElement(); 81 !element.getKind().equals(PACKAGE); 82 element = element.getEnclosingElement()) { 83 simpleNames.add(element.getSimpleName().toString()); 84 } 85 return simpleNames.build().reverse(); 86 } 87 88 static final class Factory { 89 private final Elements elements; 90 private final Messager messager; 91 Factory(Elements elements, Messager messager)92 Factory(Elements elements, Messager messager) { 93 this.elements = elements; 94 this.messager = messager; 95 } 96 createIfValid(Element element)97 Optional<AutoFactoryDeclaration> createIfValid(Element element) { 98 checkNotNull(element); 99 AnnotationMirror mirror = Mirrors.getAnnotationMirror(element, AutoFactory.class).get(); 100 checkArgument(Mirrors.getQualifiedName(mirror.getAnnotationType()). 101 contentEquals(AutoFactory.class.getName())); 102 Map<String, AnnotationValue> values = 103 Mirrors.simplifyAnnotationValueMap(elements.getElementValuesWithDefaults(mirror)); 104 checkState(values.size() == 4); 105 106 // className value is a string, so we can just call toString 107 AnnotationValue classNameValue = values.get("className"); 108 String className = classNameValue.getValue().toString(); 109 if (!className.isEmpty() && !isValidIdentifier(className)) { 110 messager.printMessage(ERROR, 111 String.format("\"%s\" is not a valid Java identifier", className), 112 element, mirror, classNameValue); 113 return Optional.absent(); 114 } 115 116 AnnotationValue extendingValue = checkNotNull(values.get("extending")); 117 TypeElement extendingType = AnnotationValues.asType(extendingValue); 118 if (extendingType == null) { 119 messager.printMessage(ERROR, "Unable to find the type: " 120 + extendingValue.getValue().toString(), 121 element, mirror, extendingValue); 122 return Optional.absent(); 123 } else if (!isValidSupertypeForClass(extendingType)) { 124 messager.printMessage(ERROR, 125 String.format("%s is not a valid supertype for a factory. " 126 + "Supertypes must be non-final classes.", 127 extendingType.getQualifiedName()), 128 element, mirror, extendingValue); 129 return Optional.absent(); 130 } 131 ImmutableList<ExecutableElement> noParameterConstructors = 132 FluentIterable.from(ElementFilter.constructorsIn(extendingType.getEnclosedElements())) 133 .filter(new Predicate<ExecutableElement>() { 134 @Override public boolean apply(ExecutableElement constructor) { 135 return constructor.getParameters().isEmpty(); 136 } 137 }) 138 .toList(); 139 if (noParameterConstructors.size() == 0) { 140 messager.printMessage(ERROR, 141 String.format("%s is not a valid supertype for a factory. " 142 + "Factory supertypes must have a no-arg constructor.", 143 extendingType.getQualifiedName()), 144 element, mirror, extendingValue); 145 return Optional.absent(); 146 } else if (noParameterConstructors.size() > 1) { 147 throw new IllegalStateException("Multiple constructors with no parameters??"); 148 } 149 150 AnnotationValue implementingValue = checkNotNull(values.get("implementing")); 151 ImmutableSet.Builder<TypeElement> builder = ImmutableSet.builder(); 152 for (AnnotationValue implementingTypeValue : AnnotationValues.asList(implementingValue)) { 153 builder.add(AnnotationValues.asType(implementingTypeValue)); 154 } 155 ImmutableSet<TypeElement> implementingTypes = builder.build(); 156 157 AnnotationValue allowSubclassesValue = checkNotNull(values.get("allowSubclasses")); 158 boolean allowSubclasses = AnnotationValues.asBoolean(allowSubclassesValue); 159 160 return Optional.<AutoFactoryDeclaration>of( 161 new AutoValue_AutoFactoryDeclaration( 162 getAnnotatedType(element), 163 element, 164 className.isEmpty() ? Optional.<String>absent() : Optional.of(className), 165 extendingType, 166 implementingTypes, 167 allowSubclasses, 168 mirror, 169 ImmutableMap.copyOf(values))); 170 } 171 getAnnotatedType(Element element)172 private static TypeElement getAnnotatedType(Element element) { 173 List<TypeElement> types = ImmutableList.of(); 174 while (types.isEmpty()) { 175 types = typesIn(Arrays.asList(element)); 176 element = element.getEnclosingElement(); 177 } 178 return getOnlyElement(types); 179 } 180 isValidIdentifier(String identifier)181 static boolean isValidIdentifier(String identifier) { 182 return SourceVersion.isIdentifier(identifier) && !SourceVersion.isKeyword(identifier); 183 } 184 } 185 } 186