aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java
blob: fe72167c5e50e9a97fa31f51c61d32299d9a1619 (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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package ftbsc.lll.processor.tools;

import com.squareup.javapoet.*;
import ftbsc.lll.tools.DescriptorBuilder;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.MirroredTypesException;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Collection of static utils that didn't really fit into the main class.
 */
public class ASTUtils {
   /**
    * Finds, among the methods of a class cl, the one annotated with ann, and tries to build
    * a {@link ExecutableElement} from it.
    * @param cl the {@link ExecutableElement} for the class containing the desired method
    * @param ann the {@link Class} corresponding to the desired annotation
    * @return a {@link List} of {@link MethodSpec}s annotated with the given annotation
    * @since 0.2.0
    */
   public static List<ExecutableElement> findAnnotatedMethods(TypeElement cl, Class<? extends Annotation> ann) {
      return cl.getEnclosedElements()
         .stream()
         .filter(e -> e.getAnnotationsByType(ann).length != 0)
         .map(e -> (ExecutableElement) e)
         .collect(Collectors.toList());
   }

   /**
    * Builds a type descriptor from the given {@link TypeName}.
    * @param type the {@link TypeName} representing the desired type
    * @return a {@link String} containing the relevant descriptor
    */
   public static String descriptorFromType(TypeName type) {
      StringBuilder desc = new StringBuilder();
      //add array brackets
      while(type instanceof ArrayTypeName) {
         desc.append("[");
         type = ((ArrayTypeName) type).componentType;
      }
      if(type instanceof ClassName || type instanceof ParameterizedTypeName) {
         ClassName var = type instanceof ParameterizedTypeName ? ((ParameterizedTypeName) type).rawType : (ClassName) type;
         desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0));
      } else {
         if(TypeName.BOOLEAN.equals(type))
            desc.append("Z");
         else if(TypeName.CHAR.equals(type))
            desc.append("C");
         else if(TypeName.BYTE.equals(type))
            desc.append("B");
         else if(TypeName.SHORT.equals(type))
            desc.append("S");
         else if(TypeName.INT.equals(type))
            desc.append("I");
         else if(TypeName.FLOAT.equals(type))
            desc.append("F");
         else if(TypeName.LONG.equals(type))
            desc.append("J");
         else if(TypeName.DOUBLE.equals(type))
            desc.append("D");
         else if(TypeName.VOID.equals(type))
            desc.append("V");
      }
      return desc.toString();
   }

   /**
    * Builds a type descriptor from the given {@link TypeMirror}.
    * @param t the {@link TypeMirror} representing the desired type
    * @return a {@link String} containing the relevant descriptor
    */
   public static String descriptorFromType(TypeMirror t) {
      return descriptorFromType(TypeName.get(t));
   }

   /**
    * Builds a method descriptor from the given {@link ExecutableElement}.
    * @param m the {@link ExecutableElement} for the method
    * @return a {@link String} containing the relevant descriptor
    */
   public static String descriptorFromExecutableElement(ExecutableElement m) {
      StringBuilder methodSignature = new StringBuilder();
      methodSignature.append("(");
      m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType())));
      methodSignature.append(")");
      methodSignature.append(descriptorFromType(m.getReturnType()));
      return methodSignature.toString();
   }

   /**
    * Maps a {@link javax.lang.model.element.Modifier} to its reflective
    * {@link java.lang.reflect.Modifier} equivalent.
    * @param m the {@link Modifier} to map
    * @return an integer representing the modifier
    * @see java.lang.reflect.Modifier
    * @since 0.2.0
    */
   public static int mapModifier(Modifier m) {
      switch(m) {
         case PUBLIC:
            return java.lang.reflect.Modifier.PUBLIC;
         case PROTECTED:
            return java.lang.reflect.Modifier.PROTECTED;
         case PRIVATE:
            return java.lang.reflect.Modifier.PRIVATE;
         case ABSTRACT:
            return java.lang.reflect.Modifier.ABSTRACT;
         case STATIC:
            return java.lang.reflect.Modifier.STATIC;
         case FINAL:
            return java.lang.reflect.Modifier.FINAL;
         case TRANSIENT:
            return java.lang.reflect.Modifier.TRANSIENT;
         case VOLATILE:
            return java.lang.reflect.Modifier.VOLATILE;
         case SYNCHRONIZED:
            return java.lang.reflect.Modifier.SYNCHRONIZED;
         case NATIVE:
            return java.lang.reflect.Modifier.NATIVE;
         case STRICTFP:
            return java.lang.reflect.Modifier.STRICT;
         default:
            return 0;
      }
   }

   /**
    * Safely extracts a {@link Class} from an annotation and gets its fully qualified name.
    * @param ann the annotation containing the class
    * @param fun the annotation function returning the class
    * @return the fully qualified name of the given class
    * @since 0.3.0
    */
   public static <T extends Annotation> String getClassFullyQualifiedName(T ann, Function<T, Class<?>> fun) {
      try {
         return fun.apply(ann).getCanonicalName();
      } catch(MirroredTypeException e) {
         return e.getTypeMirror().toString();
      }
   }

   /**
    * Safely extracts a {@link Class} array from an annotation.
    * @param ann the annotation containing the class
    * @param fun the annotation function returning the class
    * @param elementUtils the element utils corresponding to the {@link ProcessingEnvironment}
    * @return a list of {@link TypeMirror}s representing the classes
    * @since 0.3.0
    */
   public static <T extends Annotation> List<TypeMirror> classArrayFromAnnotation(T ann, Function<T, Class<?>[]> fun, Elements elementUtils) {
      List<TypeMirror> params = new ArrayList<>();
      try {
         params.addAll(Arrays.stream(fun.apply(ann))
            .map(Class::getCanonicalName)
            .map(fqn -> elementUtils.getTypeElement(fqn).asType())
            .collect(Collectors.toList()));
      } catch(MirroredTypesException e) {
         params.addAll(e.getTypeMirrors());
      }
      return params;
   }

   /**
    * Builds a (partial, not including the return type) method descriptor from its parameters
    * @param ann the annotation containing the class
    * @param fun the annotation function returning the class
    * @return the method descriptor
    */
   public static <T extends Annotation> String methodDescriptorFromParams(T ann, Function<T, Class<?>[]> fun, Elements elementUtils) {
      List<TypeMirror> mirrors = classArrayFromAnnotation(ann, fun, elementUtils);
      StringBuilder sb = new StringBuilder("(");
      for(TypeMirror t : mirrors)
         sb.append(descriptorFromType(t));
      sb.append(")");
      return sb.toString();
   }
}