package com.gabrielittner.auto.value.with; import com.gabrielittner.auto.value.util.Property; import com.google.auto.service.AutoService; import com.google.auto.value.extension.AutoValueExtension; import com.google.common.collect.ImmutableList; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeSpec; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import static com.gabrielittner.auto.value.util.AutoValueUtil.getAutoValueClassTypeName; import static com.gabrielittner.auto.value.util.AutoValueUtil.newFinalClassConstructorCall; import static com.gabrielittner.auto.value.util.AutoValueUtil.newTypeSpecBuilder; import static javax.lang.model.element.Modifier.FINAL; @AutoService(AutoValueExtension.class) public class AutoValueWithExtension extends AutoValueExtension { @Override public boolean applicable(Context context) { return WithMethod.filteredAbstractMethods(context).size() > 0; } @Override public IncrementalExtensionType incrementalType(ProcessingEnvironment processingEnvironment) { return IncrementalExtensionType.ISOLATING; } @Override public Set<ExecutableElement> consumeMethods(Context context) { return WithMethod.filteredAbstractMethods(context); } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { TypeSpec subclass = newTypeSpecBuilder(context, className, classToExtend, isFinal) .addMethods(generateWithMethods(context)) .build(); return JavaFile.builder(context.packageName(), subclass).build().toString(); } private List<MethodSpec> generateWithMethods(Context context) { List<WithMethod> withMethods = WithMethod.getWithMethods(context); ImmutableList<Property> properties = Property.buildProperties(context); List<MethodSpec> generatedMethods = new ArrayList<>(withMethods.size()); for (WithMethod withMethod : withMethods) { generatedMethods.add(generateWithMethod(withMethod, context, properties)); } return generatedMethods; } private MethodSpec generateWithMethod( WithMethod withMethod, Context context, ImmutableList<Property> properties) { String[] propertyNames = new String[properties.size()]; for (int i = 0; i < propertyNames.length; i++) { Property property = properties.get(i); if (withMethod.propertyNames.contains(property.humanName())) { propertyNames[i] = property.humanName(); } else { propertyNames[i] = property.methodName() + "()"; } } List<AnnotationSpec> annotations = new ArrayList<>(withMethod.methodAnnotations.size() + 1); for (AnnotationMirror methodAnnotation : withMethod.methodAnnotations) { annotations.add(AnnotationSpec.get(methodAnnotation)); } AnnotationSpec override = AnnotationSpec.builder(Override.class).build(); if (!annotations.contains(override)) { annotations.add(0, override); } List<Modifier> modifiers = new ArrayList<>(2); modifiers.add(FINAL); for (Modifier modifier : withMethod.methodModifiers) { if (modifier == Modifier.PUBLIC || modifier == Modifier.PROTECTED) { modifiers.add(modifier); break; } } List<ParameterSpec> parameters = new ArrayList<>(withMethod.properties.size()); for (Property property : withMethod.properties) { parameters.add(ParameterSpec.builder(property.type(), property.humanName()).build()); } return MethodSpec.methodBuilder(withMethod.methodName) .addAnnotations(annotations) .addModifiers(modifiers) .returns(getAutoValueClassTypeName(context)) .addParameters(parameters) .addCode("return ") .addCode(newFinalClassConstructorCall(context, propertyNames)) .build(); } }