aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/geb/processor/GEBProcessor.java
blob: c72bf8c7e6f7a394c26039b254f429e934950b2e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package ftbsc.geb.processor;

import ftbsc.geb.api.annotations.Event;
import ftbsc.geb.api.annotations.Listen;
import ftbsc.geb.api.annotations.ListenerInstance;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

@SupportedAnnotationTypes({"ftbsc.geb.api.annotations.*"})
public class GEBProcessor extends AbstractProcessor {
   @Override
   public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
      boolean claimed = false;
      for(TypeElement ann : set) {
         BiConsumer<GEBProcessor, Element> processMethod;
         if(ann.getQualifiedName().contentEquals(Listen.class.getName()))
            processMethod = GEBProcessor::processListener;
         else if(ann.getQualifiedName().contentEquals(Event.class.getName()))
            processMethod = GEBProcessor::processEvent;
         else continue;

         claimed = true;

         for(Element e : env.getElementsAnnotatedWith(ann))
            processMethod.accept(this, e);
      }
      return claimed;
   }

   private final Map<Element, List<ExecutableElement>> listeners = new HashMap<>();

   private static List<Element> getMembersAnnotatedWith(TypeElement typeElement, Class<? extends Annotation> ann) {
      return typeElement.getEnclosedElements()
         .stream()
         .filter(elem -> elem.getAnnotation(ann) != null)
         .collect(Collectors.toList());
   }

   private void processListener(Element target) {
      ExecutableElement listener = (ExecutableElement) target; //this will never fail
      Listen listenerAnn = target.getAnnotation(Listen.class);

      //ensure the parent is a class
      if(!(target.getEnclosingElement() instanceof TypeElement))
         return; //TODO throw error, means the annotated field was in a method
      TypeElement parent = (TypeElement) target.getEnclosingElement();

      //ensure the parent is instance of IListener
      TypeElement cursor = parent;
      TypeMirror listenerInterface = this.processingEnv.getElementUtils().getTypeElement("ftbsc.geb.api.IListener").asType()
;     while(cursor != null) {
         if(cursor.getInterfaces().contains(listenerInterface))
            break;

         Element superclass = this.processingEnv.getTypeUtils().asElement(cursor.getSuperclass());
         if(superclass instanceof TypeElement)
            cursor = (TypeElement) superclass;
         else return; //TODO throw error, parent doesnt implement the interface
      }

      List<Element> instanceSources = getMembersAnnotatedWith(parent, ListenerInstance.class);

      if(instanceSources.size() != 1)
         return; //TODO throw error, there should always be only one per class

      Element instanceSource = instanceSources.get(0);
      List<ExecutableElement> listenerList = listeners.computeIfAbsent(instanceSource, k -> new ArrayList<>());
      listenerList.add(listener);
   }

   private void processEvent(Element target) {
      //TODO
   }
}