package shortbread;
import com.google.auto.service.AutoService;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({"shortbread.Shortcut"})
public class ShortcutProcessor extends AbstractProcessor {
private boolean processed;
@Override
public boolean process(final Set<? extends TypeElement> set, final RoundEnvironment roundEnvironment) {
if (processed) {
return true;
} else {
processed = true;
}
List<ShortcutAnnotatedElement> annotatedElements = new ArrayList<>();
for (final Element element : roundEnvironment.getElementsAnnotatedWith(Shortcut.class)) {
if (element.getKind() == ElementKind.CLASS) {
final TypeElement typeElement = (TypeElement) element;
if (!isSubtypeOfActivity(typeElement.asType())) {
error(element, "Only activities can be annotated with @%s", Shortcut.class.getSimpleName());
return true;
}
annotatedElements.add(new ShortcutAnnotatedClass(typeElement));
} else if (element.getKind() == ElementKind.METHOD) {
final ExecutableElement executableElement = (ExecutableElement) element;
final Element enclosingElement = executableElement.getEnclosingElement();
if (!isSubtypeOfActivity(enclosingElement.asType())) {
error(element, "Methods annotated with @%s must be part of activities", Shortcut.class.getSimpleName());
return true;
}
if (!executableElement.getModifiers().contains(Modifier.PUBLIC)) {
error(element, "Methods annotated with @%s must be public", Shortcut.class.getSimpleName());
return true;
}
if (executableElement.getParameters().size() > 0) {
error(element, "Methods annotated with @%s can't have parameters", Shortcut.class.getSimpleName());
return true;
}
annotatedElements.add(new ShortcutAnnotatedMethod(executableElement));
} else {
error(element, "Only classes and methods can be annotated with @", Shortcut.class.getSimpleName());
return true;
}
}
new CodeGenerator(processingEnv.getFiler(), annotatedElements).generate();
return false;
}
private void error(Element element, String message, Object... args) {
if (args.length > 0) {
message = String.format(message, args);
}
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message, element);
}
private boolean isSubtypeOfActivity(TypeMirror typeMirror) {
if ("android.app.Activity".equals(typeMirror.toString())) {
return true;
}
if (typeMirror.getKind() != TypeKind.DECLARED) {
return false;
}
DeclaredType declaredType = (DeclaredType) typeMirror;
Element element = declaredType.asElement();
if (!(element instanceof TypeElement)) {
return false;
}
TypeElement typeElement = (TypeElement) element;
TypeMirror superType = typeElement.getSuperclass();
return isSubtypeOfActivity(superType);
}
}