it.frob.auto.value.neuteredtostring.AutoValueNeuterToStringExtension.java Source code

Java tutorial

Introduction

Here is the source code for it.frob.auto.value.neuteredtostring.AutoValueNeuterToStringExtension.java

Source

/*
 * Copyright (C) 2016 Alessandro Gatti
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package it.frob.auto.value.neuteredtostring;

import com.google.auto.service.AutoService;
import com.google.auto.value.extension.AutoValueExtension;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.*;
import com.squareup.javapoet.*;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Auto value extension that neuters {@link #toString()} methods for the classes it creates.
 */
@AutoService(AutoValueExtension.class)
public final class AutoValueNeuterToStringExtension extends AutoValueExtension {

    /**
     * The annotation name that triggers the extension.
     */
    private final static String ANNOTATION_NAME = "NeuteredToString";

    @Override
    public boolean applicable(Context context) {
        for (AnnotationMirror annotation : context.autoValueClass().getAnnotationMirrors()) {
            if (ANNOTATION_NAME.equals(annotation.getAnnotationType().asElement().getSimpleName().toString())) {
                return true;
            }
        }

        return false;
    }

    @Override
    public String generateClass(Context context, String className, String classToExtend, boolean isFinal) {
        String packageName = context.packageName();
        Map<String, ExecutableElement> properties = context.properties();

        TypeSpec subclass = TypeSpec.classBuilder(className)
                .addModifiers(isFinal ? Modifier.FINAL : Modifier.ABSTRACT)
                .superclass(ClassName.get(packageName, classToExtend)).addMethod(generateConstructor(properties))
                .addMethod(generateToString()).build();

        JavaFile javaFile = JavaFile.builder(packageName, subclass).build();
        return javaFile.toString();
    }

    /**
     * Class constructor method generator.
     *
     * @param properties the properties of the class constructor to generate.
     * @return a {@link MethodSpec} instance containing the class constructor.
     */
    private static MethodSpec generateConstructor(Map<String, ExecutableElement> properties) {
        List<ParameterSpec> params = new ArrayList<ParameterSpec>();
        for (Map.Entry<String, ExecutableElement> entry : properties.entrySet()) {
            TypeName typeName = TypeName.get(entry.getValue().getReturnType());
            params.add(ParameterSpec.builder(typeName, entry.getKey()).build());
        }

        String body = String.format("super(%s)",
                Joiner.on(", ")
                        .join(FluentIterable
                                .from(ImmutableRangeSet.of(Range.closedOpen(0, properties.size()))
                                        .asSet(DiscreteDomain.integers()).descendingSet())
                                .transform(new Function<Integer, String>() {
                                    @Override
                                    public String apply(Integer input) {
                                        return "$N";
                                    }
                                })));

        return MethodSpec.constructorBuilder().addParameters(params)
                .addStatement(body, properties.keySet().toArray()).build();
    }

    /**
     * Neutered toString method generator.
     *
     * @return a {@link MethodSpec} instance containing a neutered toString method.
     */
    private static MethodSpec generateToString() {
        return MethodSpec.methodBuilder("toString").addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL).returns(String.class)
                .addCode("return getClass().getName() + \"@\" + Integer.toHexString(hashCode());\n").build();
    }
}