tiger.DependencyInformationCollectorProcessor.java Source code

Java tutorial

Introduction

Here is the source code for tiger.DependencyInformationCollectorProcessor.java

Source

// Copyright 2016 Google Inc.
//
// 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 tiger;

import com.google.auto.service.AutoService;
import com.google.common.collect.Iterables;
import com.google.common.io.BaseEncoding;
import com.google.gson.Gson;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import dagger.Module;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.inject.Inject;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

/**
 * Annotation processor to collect dependency information. All the information
 * are written into java files in package
 * {@link SharedNames#DEPENDENCY_INFORMATION_PACKAGE_NAME} which is later used as
 * input for {@link ComponentGeneratorProcessor}. Three piece of information are
 * collected: {@link Module}s, {@link MembersInjector}s and ctor {@link Inject}
 * ed scoped classes. A class is created as container. For each piece of
 * information collected, either a class or a interface, a field of that type is
 * added to the container.
 */
@AutoService(Processor.class)
public class DependencyInformationCollectorProcessor extends AbstractProcessor {
    private static final String TAG = "DependencyInformationCollectorProcessor";

    private Gson gson = new Gson();

    private Filer filer;
    private Messager messager;

    // This might depend on some Classes generated by other processor. Delay to
    // the next round if needed.
    private boolean done;

    private boolean foundSomething;

    private String uniqueSuffix;

    // Following could be used cross processing rounds.
    private Set<String> moduleStrings = new HashSet<>();
    private Set<String> membersInjectors = new HashSet<>();
    private Set<String> scopeDependencies = new HashSet<>();
    private Set<String> scopedComponentNames = new HashSet<>();
    private Set<String> ctorInjectedClassStrings = new HashSet<>();

    @Override
    public synchronized void init(ProcessingEnvironment env) {
        super.init(env);
        filer = env.getFiler();
        messager = env.getMessager();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        if (annotations.isEmpty()) {
            return false;
        }

        boolean foundSomething = false;

        Element someElement = Iterables
                .getFirst(env.getElementsAnnotatedWith(Iterables.getFirst(annotations, null)), null);
        ElementKind elementKind = someElement.getKind();
        while (!(elementKind.equals(ElementKind.CLASS) || elementKind.equals(ElementKind.INTERFACE))) {
            someElement = someElement.getEnclosingElement();
            elementKind = someElement.getKind();
        }
        String suffix = ((TypeElement) someElement).getQualifiedName().toString().replace(".", "_");
        String uniqueSuffix = suffix + BaseEncoding.base64().encode(UUID.randomUUID().toString().getBytes());

        TypeSpec.Builder dependencyInfoCollectedBuilder = TypeSpec.classBuilder("DependencyInfo_" + uniqueSuffix)
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL);

        for (Element element : env.getElementsAnnotatedWith(Module.class)) {
            foundSomething = true;
            moduleStrings.add(element.toString());
        }
        String jsonString = gson.toJson(moduleStrings);
        dependencyInfoCollectedBuilder
                .addField(FieldSpec.builder(String.class, SharedNames.DEPENDENCY_INFORMATION_FIELD_NAME_MODULES,
                        Modifier.PUBLIC, Modifier.FINAL).initializer("$S", jsonString).build());

        for (Element element : env.getElementsAnnotatedWith(MembersInjector.class)) {
            foundSomething = true;
            membersInjectors.add(element.toString());
        }
        jsonString = gson.toJson(membersInjectors);
        dependencyInfoCollectedBuilder.addField(
                FieldSpec.builder(String.class, SharedNames.DEPENDENCY_INFORMATION_FIELD_NAME_MEMBERS_INJECTORS,
                        Modifier.PUBLIC, Modifier.FINAL).initializer("$S", jsonString).build());

        for (Element element : env.getElementsAnnotatedWith(ScopeDependency.class)) {
            foundSomething = true;
            scopeDependencies.add(element.toString());
        }
        if (!scopeDependencies.isEmpty()) {
            jsonString = gson.toJson(scopeDependencies);
            dependencyInfoCollectedBuilder.addField(FieldSpec
                    .builder(String.class, SharedNames.DEPENDENCY_INFORMATION_FIELD_NAME_SCOPE_DEPENDENCIES,
                            Modifier.PUBLIC, Modifier.FINAL)
                    .initializer("$S", jsonString).build());
        }

        for (Element element : env.getElementsAnnotatedWith(ScopedComponentNames.class)) {
            foundSomething = true;
            scopedComponentNames.add(element.toString());
        }
        if (!scopedComponentNames.isEmpty()) {
            jsonString = gson.toJson(scopedComponentNames);
            dependencyInfoCollectedBuilder.addField(FieldSpec
                    .builder(String.class, SharedNames.DEPENDENCY_INFORMATION_FIELD_NAME_SCOPED_COMPONENT_NAMES,
                            Modifier.PUBLIC, Modifier.FINAL)
                    .initializer("$S", jsonString).build());
        }

        for (Element element : env.getElementsAnnotatedWith(PackageForGenerated.class)) {
            foundSomething = true;
            jsonString = getPackageForGenerated((TypeElement) element);
            dependencyInfoCollectedBuilder.addField(FieldSpec
                    .builder(String.class, SharedNames.DEPENDENCY_INFORMATION_FIELD_NAME_PACKAGE_FOR_GENERATED,
                            Modifier.PUBLIC, Modifier.FINAL)
                    .initializer("$S", jsonString).build());
        }

        for (Element element : env.getElementsAnnotatedWith(Inject.class)) {
            if (element.getKind().equals(ElementKind.CONSTRUCTOR)) {
                Element classElement = element.getEnclosingElement();
                if (Utils.getScopeType(classElement) != null) {
                    foundSomething = true;
                    ctorInjectedClassStrings.add(classElement.toString());
                }
            }
        }
        jsonString = gson.toJson(ctorInjectedClassStrings);
        dependencyInfoCollectedBuilder.addField(
                FieldSpec.builder(String.class, SharedNames.DEPENDENCY_INFORMATION_FIELD_NAME_CTOR_INJECTED_CLASSES,
                        Modifier.PUBLIC, Modifier.FINAL).initializer("$S", jsonString).build());

        if (foundSomething) {
            writeJavaFile(SharedNames.DEPENDENCY_INFORMATION_PACKAGE_NAME, dependencyInfoCollectedBuilder.build());
        }
        return false;
    }

    private String getPackageForGenerated(TypeElement typeElement) {
        AnnotationMirror amAnnotationMirror = Utils.getAnnotationMirror(typeElement, PackageForGenerated.class);
        return (String) Utils.getAnnotationValue(amAnnotationMirror, "value").getValue();
    }

    private void writeJavaFile(String packageName, TypeSpec typeSpec) {
        JavaFile file = JavaFile.builder(packageName, typeSpec).build();

        try {
            file.writeTo(filer);
        } catch (IOException e) {
            messager.printMessage(Kind.ERROR,
                    String.format("Generating modules class failed. %s, %s, package %s TypeSpec: %s", e, file,
                            packageName, typeSpec));
            throw new RuntimeException(e);
        }
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> result = new HashSet<>();
        result.add(Module.class.getCanonicalName());
        result.add(MembersInjector.class.getCanonicalName());
        result.add(Inject.class.getCanonicalName());
        result.add(ScopeDependency.class.getCanonicalName());
        result.add(ScopedComponentNames.class.getCanonicalName());
        result.add(PackageForGenerated.class.getCanonicalName());
        return result;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}