import com.google.auto.service.AutoService; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import java.util.*; import java.util.stream.Collectors; import hdvtdev.telegram.handler.annotations.*; @AutoService(Processor.class) @SupportedAnnotationTypes({ "hdvtdev.telegram.handler.annotations.OnChosenInlineResult", "hdvtdev.telegram.handler.annotations.OnChatMemberUpdated", "hdvtdev.telegram.handler.annotations.OnChatBoostRemoved", "hdvtdev.telegram.handler.annotations.OnPreCheckoutQuery", "hdvtdev.telegram.handler.annotations.OnBusinessConnection", "hdvtdev.telegram.handler.annotations.OnPaidMediaPurchased", "hdvtdev.telegram.handler.annotations.OnUpdate", "hdvtdev.telegram.handler.annotations.BotCommand", "hdvtdev.telegram.handler.annotations.OnPoll", "hdvtdev.telegram.handler.annotations.OnMessage", "hdvtdev.telegram.handler.annotations.OnChatJoinRequest", "hdvtdev.telegram.handler.annotations.OnChatBoostUpdated", "hdvtdev.telegram.handler.annotations.OnCallbackQuery", "hdvtdev.telegram.handler.annotations.OnShippingQuery", "hdvtdev.telegram.handler.annotations.OnInlineQuery", "hdvtdev.telegram.handler.annotations.OnPollAnswer"}) @SupportedSourceVersion(SourceVersion.RELEASE_21) public class EventHandlersProcessor extends AbstractProcessor { private final StringBuilder sb = new StringBuilder(); private static final String PACKAGE = "hdvtdev.telegram.handler.annotations."; private static final String HANDLER_PACKAGE = "hdvtdev.telegram.handler."; private static final Set SUPPORTED_ANNOTATIONS = Set.of( OnChosenInlineResult.class, OnChatMemberUpdated.class, OnChatBoostRemoved.class, OnPreCheckoutQuery.class, OnBusinessConnection.class, OnPaidMediaPurchased.class, OnUpdate.class, OnPoll.class, OnMessage.class, OnChatJoinRequest.class, OnChatBoostUpdated.class, OnCallbackQuery.class, OnShippingQuery.class, OnInlineQuery.class, OnPollAnswer.class ).stream().map(Class::getName).collect(Collectors.toSet()); private static final Map SUPPORTED_PARAMETERS = Map.ofEntries( Map.entry("Update", "hdvtdev.telegram.core.objects.Update"), Map.entry("InlineQuery", "hdvtdev.telegram.core.objects.InlineQuery"), Map.entry("ChatMemberUpdated", "hdvtdev.telegram.core.objects.chat.ChatMemberUpdated"), Map.entry("ChatBoostRemoved", "hdvtdev.telegram.core.objects.chatboost.ChatBoostRemoved"), Map.entry("PreCheckoutQuery", "hdvtdev.telegram.core.objects.payment.PreCheckoutQuery"), Map.entry("BusinessConnection", "hdvtdev.telegram.core.objects.business.BusinessConnection"), Map.entry("PaidMediaPurchased", "hdvtdev.telegram.core.objects.media.paidmedia.PaidMediaPurchased"), Map.entry("Poll", "hdvtdev.telegram.core.objects.poll.Poll"), Map.entry("Message", "hdvtdev.telegram.core.objects.Message"), Map.entry("ChatJoinRequest", "hdvtdev.telegram.core.objects.chat.ChatJoinRequest"), Map.entry("ChatBoostUpdated", "hdvtdev.telegram.core.objects.chatboost.ChatBoostUpdated"), Map.entry("CallbackQuery", "hdvtdev.telegram.core.objects.callback.CallbackQuery"), Map.entry("ShippingQuery", "hdvtdev.telegram.core.objects.payment.ShippingQuery"), Map.entry("PollAnswer", "hdvtdev.telegram.core.objects.poll.PollAnswer"), Map.entry("ChosenInlineResult", "hdvtdev.telegram.core.objects.ChosenInlineResult") ); public static String getFullParameterName(String simpleName) { return SUPPORTED_PARAMETERS.get(simpleName); } private Messager messager; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); messager = processingEnv.getMessager(); } @Override public boolean process(Set set, RoundEnvironment roundEnvironment) { messager.printNote("Called"); for (Element element : roundEnvironment.getRootElements()) { for (Element enclosedElement : element.getEnclosedElements()) { if (enclosedElement.getKind() == ElementKind.METHOD) { processMethod((ExecutableElement) enclosedElement); } } } if (roundEnvironment.processingOver()) { write(); } return true; } private void write() { } private void processMethod(ExecutableElement methodElement) { String methodName = methodElement.getSimpleName().toString(); List foundAnnotations = new ArrayList<>(); for (AnnotationMirror mirror : methodElement.getAnnotationMirrors()) { String mirrorAnnotationName = Util.getAnnotationMirrorName(mirror); if (SUPPORTED_ANNOTATIONS.contains(PACKAGE + mirrorAnnotationName)) { foundAnnotations.add(mirror); } } if (foundAnnotations.size() > 1) { List annotationNames = new ArrayList<>(); for (AnnotationMirror mirror : foundAnnotations) { annotationNames.add("@" + Util.getAnnotationMirrorName(mirror)); } messager.printError(String.format("Method %s cannot have multiple mutually exclusive annotations. Found: %s. Please leave only one.", methodName, String.join(", ", annotationNames)), methodElement); return; } if (foundAnnotations.size() == 1) { AnnotationMirror annotation = foundAnnotations.getFirst(); String annotationSimpleName = Util.getAnnotationMirrorName(annotation); var params = methodElement.getParameters(); if (params.isEmpty()) { messager.printError(String.format("Method %s, annotated with @%s, must have at least one parameter.", methodName, annotationSimpleName), methodElement); return; } String firstParameterType = Util.getParameterClassName(params.getFirst().asType()); String requiredParameter = Util.checkParameter(annotationSimpleName, firstParameterType); if (!requiredParameter.isEmpty()) { messager.printError(String.format("Incorrect parameter type for method %s. Annotation @%s requires %s, but found %s.", methodName, annotationSimpleName, requiredParameter, firstParameterType), params.getFirst()); return; } if (!annotationSimpleName.equals("BotCommand")) { String paramName = params.getFirst().getSimpleName().toString(); Set filters = new HashSet<>(); AnnotationValue vv = Util.getAnnotationValue(annotation, "filters"); @SuppressWarnings("unchecked") List enumValues = vv == null ? List.of() : (List) vv.getValue(); for (AnnotationValue v : enumValues) { if (v.getValue() instanceof VariableElement enumConstant) { filters.add(enumConstant.getSimpleName().toString()); } } sb.append(Generator.generateVoid(methodName, String.format("%s %s", firstParameterType, paramName), Generator.generateFilterBlock(filters, paramName))); } } } }