/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.litho.specmodels.processor;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.facebook.litho.annotations.LayoutSpec;
import com.facebook.litho.annotations.MountSpec;
import com.facebook.litho.specmodels.model.ClassNames;
import com.facebook.litho.specmodels.model.DependencyInjectionHelper;
import com.facebook.litho.specmodels.model.SpecModel;
import com.facebook.litho.specmodels.model.SpecModelValidationError;
import com.squareup.javapoet.JavaFile;
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public abstract class AbstractComponentsProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
Set<Element> layoutSpecElements =
(Set<Element>) roundEnv.getElementsAnnotatedWith(LayoutSpec.class);
Set<Element> mountSpecElements =
(Set<Element>) roundEnv.getElementsAnnotatedWith(MountSpec.class);
Set<Element> allSpecElements = new LinkedHashSet<>();
allSpecElements.addAll(layoutSpecElements);
allSpecElements.addAll(mountSpecElements);
for (Element element : allSpecElements) {
try {
SpecModel specModel = null;
final TypeElement typeElement = (TypeElement) element;
if (element.getAnnotation(LayoutSpec.class) != null) {
specModel = getLayoutSpecModel(typeElement);
} else if (element.getAnnotation(MountSpec.class) != null) {
specModel =
MountSpecModelFactory.create(
processingEnv.getElementUtils(),
(TypeElement) element,
getDependencyInjectionGenerator((TypeElement) element));
}
if (specModel != null) {
validate(specModel);
generate(specModel);
}
} catch (PrintableException e) {
e.print(processingEnv.getMessager());
} catch (Exception e) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.ERROR,
"Unexpected error thrown when generating this component spec. " +
"Please report stack trace to the components team.",
element);
e.printStackTrace();
}
}
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return new LinkedHashSet<>(Arrays.asList(
ClassNames.LAYOUT_SPEC.toString(),
ClassNames.MOUNT_SPEC.toString()));
}
abstract protected DependencyInjectionHelper getDependencyInjectionGenerator(
TypeElement typeElement);
abstract protected SpecModel getLayoutSpecModel(TypeElement typeElement);
void validate(SpecModel specModel) {
List<SpecModelValidationError> validationErrors = specModel.validate();
if (validationErrors.isEmpty()) {
return;
}
final List<PrintableException> printableExceptions = new ArrayList<>();
for (SpecModelValidationError validationError : validationErrors) {
printableExceptions.add(
new ComponentsProcessingException(
(Element) validationError.element,
validationError.message));
}
throw new MultiPrintableException(printableExceptions);
}
protected void generate(SpecModel specModel) throws IOException {
JavaFile.builder(
getPackageName(specModel.getComponentTypeName().toString()), specModel.generate())
.skipJavaLangImports(true)
.build()
.writeTo(processingEnv.getFiler());
}
protected static String getPackageName(String qualifiedName) {
return qualifiedName.substring(0, qualifiedName.lastIndexOf('.'));
}
}