toothpick.compiler.memberinjector.MemberInjectorProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of toothpick-compiler Show documentation
Show all versions of toothpick-compiler Show documentation
'Annotation Processors of toothpick'
The newest version!
/*
* Copyright 2019 Stephane Nicolas
* Copyright 2019 Daniel Molinero Reguera
*
* 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 toothpick.compiler.memberinjector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.inject.Inject;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import toothpick.MemberInjector;
import toothpick.compiler.common.ToothpickProcessor;
import toothpick.compiler.memberinjector.generators.MemberInjectorGenerator;
import toothpick.compiler.memberinjector.targets.FieldInjectionTarget;
import toothpick.compiler.memberinjector.targets.MethodInjectionTarget;
/**
* This processor's role is to create {@link MemberInjector}. We create factories in different
* situations :
*
*
* - When a class {@code Foo} has an {@link javax.inject.Singleton} annotated field :
* --> we create a MemberInjector to inject {@code Foo} instances.
* - When a class {@code Foo} has an {@link javax.inject.Singleton} method :
* --> we create a MemberInjector to inject {@code Foo} instances.
*
*/
// http://stackoverflow.com/a/2067863/693752
@SupportedAnnotationTypes({ToothpickProcessor.INJECT_ANNOTATION_CLASS_NAME})
@SupportedOptions({
ToothpickProcessor.PARAMETER_EXCLUDES, //
ToothpickProcessor.PARAMETER_CRASH_WHEN_INJECTED_METHOD_IS_NOT_PACKAGE
}) //
public class MemberInjectorProcessor extends ToothpickProcessor {
private Map> mapTypeElementToFieldInjectorTargetList;
private Map> mapTypeElementToMethodInjectorTargetList;
private Map mapTypeElementToSuperTypeElementThatNeedsInjection;
private Map allRoundsGeneratedToTypeElement = new HashMap<>();
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
readCommonProcessorOptions();
readOptionCrashWhenMethodIsNotPackageProtected();
mapTypeElementToFieldInjectorTargetList = new LinkedHashMap<>();
mapTypeElementToMethodInjectorTargetList = new LinkedHashMap<>();
mapTypeElementToSuperTypeElementThatNeedsInjection = new LinkedHashMap<>();
findAndParseTargets(roundEnv);
// Generate member scopes
Set elementWithInjectionSet = new HashSet<>();
elementWithInjectionSet.addAll(mapTypeElementToFieldInjectorTargetList.keySet());
elementWithInjectionSet.addAll(mapTypeElementToMethodInjectorTargetList.keySet());
for (TypeElement typeElement : elementWithInjectionSet) {
List fieldInjectionTargetList =
mapTypeElementToFieldInjectorTargetList.get(typeElement);
List methodInjectionTargetList =
mapTypeElementToMethodInjectorTargetList.get(typeElement);
TypeElement superClassThatNeedsInjection =
mapTypeElementToSuperTypeElementThatNeedsInjection.get(typeElement);
MemberInjectorGenerator memberInjectorGenerator =
new MemberInjectorGenerator(
typeElement, //
superClassThatNeedsInjection, //
fieldInjectionTargetList, //
methodInjectionTargetList, //
typeUtils);
String fileDescription = String.format("MemberInjector for type %s", typeElement);
writeToFile(memberInjectorGenerator, fileDescription, typeElement);
allRoundsGeneratedToTypeElement.put(memberInjectorGenerator.getFqcn(), typeElement);
}
return false;
}
private void readOptionCrashWhenMethodIsNotPackageProtected() {
Map options = processingEnv.getOptions();
if (toothpickCrashWhenMethodIsNotPackageVisible == null) {
toothpickCrashWhenMethodIsNotPackageVisible =
Boolean.parseBoolean(options.get(PARAMETER_CRASH_WHEN_INJECTED_METHOD_IS_NOT_PACKAGE));
}
}
private void findAndParseTargets(RoundEnvironment roundEnv) {
processInjectAnnotatedFields(roundEnv);
processInjectAnnotatedMethods(roundEnv);
}
protected void processInjectAnnotatedFields(RoundEnvironment roundEnv) {
for (VariableElement element :
ElementFilter.fieldsIn(roundEnv.getElementsAnnotatedWith(Inject.class))) {
if (!isExcludedByFilters((TypeElement) element.getEnclosingElement())) {
processInjectAnnotatedField(element, mapTypeElementToFieldInjectorTargetList);
}
}
}
protected void processInjectAnnotatedMethods(RoundEnvironment roundEnv) {
for (ExecutableElement element :
ElementFilter.methodsIn(roundEnv.getElementsAnnotatedWith(Inject.class))) {
if (!isExcludedByFilters((TypeElement) element.getEnclosingElement())) {
processInjectAnnotatedMethod(element, mapTypeElementToMethodInjectorTargetList);
}
}
}
private void processInjectAnnotatedField(
VariableElement fieldElement,
Map> mapTypeElementToMemberInjectorTargetList) {
TypeElement enclosingElement = (TypeElement) fieldElement.getEnclosingElement();
// Verify common generated code restrictions.
if (!isValidInjectAnnotatedFieldOrParameter(fieldElement)) {
return;
}
List fieldInjectionTargetList =
mapTypeElementToMemberInjectorTargetList.get(enclosingElement);
if (fieldInjectionTargetList == null) {
fieldInjectionTargetList = new ArrayList<>();
mapTypeElementToMemberInjectorTargetList.put(enclosingElement, fieldInjectionTargetList);
}
mapTypeToMostDirectSuperTypeThatNeedsInjection(enclosingElement);
fieldInjectionTargetList.add(createFieldOrParamInjectionTarget(fieldElement));
}
private void processInjectAnnotatedMethod(
ExecutableElement methodElement,
Map> mapTypeElementToMemberInjectorTargetList) {
TypeElement enclosingElement = (TypeElement) methodElement.getEnclosingElement();
// Verify common generated code restrictions.
if (!isValidInjectAnnotatedMethod(methodElement)) {
return;
}
List methodInjectionTargetList =
mapTypeElementToMemberInjectorTargetList.get(enclosingElement);
if (methodInjectionTargetList == null) {
methodInjectionTargetList = new ArrayList<>();
mapTypeElementToMemberInjectorTargetList.put(enclosingElement, methodInjectionTargetList);
}
mapTypeToMostDirectSuperTypeThatNeedsInjection(enclosingElement);
methodInjectionTargetList.add(createMethodInjectionTarget(methodElement));
}
private void mapTypeToMostDirectSuperTypeThatNeedsInjection(TypeElement typeElement) {
TypeElement superClassWithInjectedMembers =
getMostDirectSuperClassWithInjectedMembers(typeElement, true);
mapTypeElementToSuperTypeElementThatNeedsInjection.put(
typeElement, superClassWithInjectedMembers);
}
private MethodInjectionTarget createMethodInjectionTarget(ExecutableElement methodElement) {
TypeElement enclosingElement = (TypeElement) methodElement.getEnclosingElement();
final String methodName = methodElement.getSimpleName().toString();
boolean isOverride = isOverride(enclosingElement, methodElement);
MethodInjectionTarget methodInjectionTarget =
new MethodInjectionTarget(enclosingElement, methodName, isOverride);
methodInjectionTarget.parameters.addAll(getParamInjectionTargetList(methodElement));
methodInjectionTarget.exceptionTypes.addAll(getExceptionTypes(methodElement));
return methodInjectionTarget;
}
// used for testing only
void setToothpickExcludeFilters(String toothpickExcludeFilters) {
this.toothpickExcludeFilters = toothpickExcludeFilters;
}
// used for testing only
void setCrashOrWarnWhenMethodIsNotPackageVisible(
boolean crashOrWarnWhenMethodIsNotPackageVisible) {
this.toothpickCrashWhenMethodIsNotPackageVisible = crashOrWarnWhenMethodIsNotPackageVisible;
}
// used for testing only
TypeElement getOriginatingElement(String generatedQualifiedName) {
return allRoundsGeneratedToTypeElement.get(generatedQualifiedName);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy