Java tutorial
// 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(); } }