1111import io .jooby .MediaType ;
1212import io .jooby .Reified ;
1313import io .jooby .Route ;
14- import io .jooby .SneakyThrows ;
1514import org .objectweb .asm .ClassWriter ;
1615import org .objectweb .asm .Label ;
1716import org .objectweb .asm .MethodVisitor ;
18- import org .objectweb .asm .Opcodes ;
1917import org .objectweb .asm .Type ;
2018
2119import javax .annotation .processing .ProcessingEnvironment ;
22- import javax .lang .model .element .AnnotationMirror ;
23- import javax .lang .model .element .AnnotationValue ;
24- import javax .lang .model .element .ExecutableElement ;
25- import javax .lang .model .type .TypeMirror ;
26- import javax .lang .model .util .Elements ;
27- import javax .lang .model .util .Types ;
2820import java .lang .reflect .Method ;
29- import java .util .ArrayList ;
30- import java .util .HashMap ;
3121import java .util .List ;
32- import java .util .Map ;
33- import java .util .function .Predicate ;
34- import java .util .stream .Collectors ;
3522
36- import static io .jooby .SneakyThrows .throwingConsumer ;
37- import static java .util .Collections .singletonList ;
3823import static org .objectweb .asm .Opcodes .ACC_PUBLIC ;
3924import static org .objectweb .asm .Opcodes .ACC_SUPER ;
4025import static org .objectweb .asm .Opcodes .ACC_SYNTHETIC ;
4126import static org .objectweb .asm .Opcodes .ALOAD ;
4227import static org .objectweb .asm .Opcodes .ASTORE ;
43- import static org .objectweb .asm .Opcodes .DCONST_0 ;
44- import static org .objectweb .asm .Opcodes .DCONST_1 ;
4528import static org .objectweb .asm .Opcodes .DUP ;
46- import static org .objectweb .asm .Opcodes .FCONST_0 ;
47- import static org .objectweb .asm .Opcodes .FCONST_1 ;
48- import static org .objectweb .asm .Opcodes .FCONST_2 ;
4929import static org .objectweb .asm .Opcodes .GETFIELD ;
5030import static org .objectweb .asm .Opcodes .GETSTATIC ;
51- import static org .objectweb .asm .Opcodes .ICONST_0 ;
52- import static org .objectweb .asm .Opcodes .ICONST_1 ;
53- import static org .objectweb .asm .Opcodes .ICONST_2 ;
54- import static org .objectweb .asm .Opcodes .ICONST_3 ;
55- import static org .objectweb .asm .Opcodes .ICONST_4 ;
56- import static org .objectweb .asm .Opcodes .ICONST_5 ;
5731import static org .objectweb .asm .Opcodes .INVOKESPECIAL ;
5832import static org .objectweb .asm .Opcodes .INVOKESTATIC ;
5933import static org .objectweb .asm .Opcodes .INVOKEVIRTUAL ;
60- import static org .objectweb .asm .Opcodes .LCONST_0 ;
61- import static org .objectweb .asm .Opcodes .LCONST_1 ;
6234import static org .objectweb .asm .Opcodes .NEW ;
6335import static org .objectweb .asm .Opcodes .POP ;
6436import static org .objectweb .asm .Opcodes .RETURN ;
@@ -70,31 +42,18 @@ public class ModuleCompiler {
7042 private static final Type OBJ = getType (Object .class );
7143 private static final Type MVC_EXTENSION = getType (Extension .class );
7244
73- private static final Predicate <String > HTTP_ANNOTATION = it ->
74- it .startsWith ("io.jooby.annotations" )
75- || it .startsWith ("javax.ws.rs" );
76-
77- private static final Predicate <String > NULL_ANNOTATION = it -> it .endsWith ("NonNull" )
78- || it .endsWith ("NotNull" )
79- || it .endsWith ("Nullable" );
80-
81- private static final Predicate <String > ATTR_FILTER = HTTP_ANNOTATION .negate ()
82- .and (NULL_ANNOTATION .negate ());
83-
8445 private final String controllerClass ;
8546 private final String moduleClass ;
8647 private final String moduleInternalName ;
8748 private final String moduleJava ;
88- private final Types types ;
89- private final Elements elements ;
49+ private final ProcessingEnvironment env ;
9050
9151 public ModuleCompiler (ProcessingEnvironment env , String controllerClass ) {
9252 this .controllerClass = controllerClass ;
9353 this .moduleClass = this .controllerClass + "$Module" ;
9454 this .moduleJava = this .moduleClass + ".java" ;
9555 this .moduleInternalName = moduleClass .replace ("." , "/" );
96- this .types = env .getTypeUtils ();
97- this .elements = env .getElementUtils ();
56+ this .env = env ;
9857 }
9958
10059 public String getModuleClass () {
@@ -127,6 +86,9 @@ private void install(ClassWriter writer, List<HandlerCompiler> handlers) throws
12786 Label sourceStart = new Label ();
12887 visitor .visitLabel (sourceStart );
12988
89+ RouteAttributesWriter routeAttributes = new RouteAttributesWriter (env .getElementUtils (),
90+ env .getTypeUtils (), writer , moduleInternalName , visitor );
91+
13092 for (HandlerCompiler handler : handlers ) {
13193 visitor .visitVarInsn (ALOAD , 1 );
13294 visitor .visitLdcInsn (handler .getPattern ());
@@ -157,175 +119,15 @@ private void install(ClassWriter writer, List<HandlerCompiler> handlers) throws
157119 setContentType (visitor , "setProduces" , handler .getProduces ());
158120
159121 /**
160- * Annotations
122+ * Annotations as route attributes
161123 */
162- setAnnotations ( visitor , handler );
124+ routeAttributes . process ( handler . getExecutable () );
163125 }
164126 visitor .visitInsn (RETURN );
165127 visitor .visitMaxs (0 , 0 );
166128 visitor .visitEnd ();
167129 }
168130
169- private Map <String , Object > annotationMap (ExecutableElement method ) {
170- return annotationMap (method .getAnnotationMirrors (), null );
171- }
172-
173- private Map <String , Object > annotationMap (List <? extends AnnotationMirror > annotations ,
174- String root ) {
175- Map <String , Object > result = new HashMap <>();
176- for (AnnotationMirror annotation : annotations ) {
177- if (!ATTR_FILTER .test (annotation .getAnnotationType ().toString ())) {
178- // Ignore core,jars annnotations
179- continue ;
180- }
181- String prefix = root == null
182- ? annotation .getAnnotationType ().asElement ().getSimpleName ().toString ()
183- : root ;
184- Map <? extends ExecutableElement , ? extends AnnotationValue > values = elements
185- .getElementValuesWithDefaults (annotation );
186- for (Map .Entry <? extends ExecutableElement , ? extends AnnotationValue > attribute : values
187- .entrySet ()) {
188- ExecutableElement key = attribute .getKey ();
189- String method = key .getSimpleName ().toString ();
190- String name = method .equals ("value" ) ? prefix : prefix + "." + method ;
191- Object value = annotationValue (attribute .getValue ());
192- if (value != null ) {
193- result .put (name , value );
194- }
195- }
196- }
197- return result ;
198- }
199-
200- private Object annotationValue (AnnotationValue annotationValue ) {
201- Object value = annotationValue .getValue ();
202- if (value instanceof AnnotationMirror ) {
203- Map <String , Object > annotation = annotationMap (singletonList ((AnnotationMirror ) value ), null );
204- return annotation .isEmpty () ? null : annotation ;
205- } else if (value instanceof List ) {
206- List <AnnotationValue > values = (List ) value ;
207- if (values .size () > 0 ) {
208- List <Object > result = new ArrayList <>();
209- for (AnnotationValue it : values ) {
210- result .add (annotationValue (it ));
211- }
212- return result ;
213- }
214- return null ;
215- }
216- return value ;
217- }
218-
219- private void setAnnotations (MethodVisitor visitor , HandlerCompiler handler )
220- throws NoSuchMethodException {
221- Method target = Route .class .getDeclaredMethod ("attribute" , String .class , Object .class );
222- Map <String , Object > attributes = annotationMap (handler .getExecutable ());
223- for (Map .Entry <String , Object > attribute : attributes .entrySet ()) {
224- String name = attribute .getKey ();
225- Object value = attribute .getValue ();
226-
227- if (value instanceof Map ) {
228- // ignore annotation attribute
229- continue ;
230- }
231-
232- visitor .visitVarInsn (ALOAD , 2 );
233- visitor .visitLdcInsn (name );
234- if (value instanceof List ) {
235- List values = (List ) value ;
236- if (values .size () > 0 ) {
237- ArrayWriter .write (visitor , values .get (0 ).getClass (), values , throwingConsumer (v ->
238- annotationSingleValue (visitor , v )
239- ));
240- visitor .visitMethodInsn (INVOKESTATIC , "java/util/Arrays" , "asList" ,
241- "([Ljava/lang/Object;)Ljava/util/List;" , false );
242- }
243- } else {
244- annotationSingleValue (visitor , value );
245- }
246- visitor .visitMethodInsn (INVOKEVIRTUAL , Type .getInternalName (target .getDeclaringClass ()),
247- target .getName (), Type .getMethodDescriptor (target ), false );
248- visitor .visitInsn (POP );
249- }
250- }
251-
252- private void annotationSingleValue (MethodVisitor visitor , Object value )
253- throws NoSuchMethodException {
254- if (value instanceof String ) {
255- visitor .visitLdcInsn (value );
256- } else if (value instanceof Boolean ) {
257- annotationBoolean (visitor , (Boolean ) value , false , ICONST_0 , ICONST_1 );
258- } else if (value instanceof Character ) {
259- annotationCharacter (visitor , (Character ) value , false ,
260- ICONST_0 , ICONST_1 , ICONST_2 , ICONST_3 , ICONST_4 , ICONST_5 );
261- } else if (value instanceof Short ) {
262- annotationNumber (visitor , (Number ) value , true ,
263- ICONST_0 , ICONST_1 , ICONST_2 , ICONST_3 , ICONST_4 , ICONST_5 );
264- } else if (value instanceof Integer ) {
265- annotationNumber (visitor , (Number ) value , true ,
266- ICONST_0 , ICONST_1 , ICONST_2 , ICONST_3 , ICONST_4 , ICONST_5 );
267- } else if (value instanceof Long ) {
268- annotationNumber (visitor , (Number ) value , false , LCONST_0 , LCONST_1 );
269- } else if (value instanceof Float ) {
270- annotationNumber (visitor , (Number ) value , false , FCONST_0 , FCONST_1 ,
271- FCONST_2 );
272- } else if (value instanceof Double ) {
273- annotationNumber (visitor , (Number ) value , false , DCONST_0 , DCONST_1 );
274- } else if (value instanceof TypeMirror ) {
275- TypeDefinition typeDef = new TypeDefinition (types , (TypeMirror ) value );
276- if (typeDef .isPrimitive ()) {
277- Method wrapper = Primitives .wrapper (typeDef );
278- visitor .visitFieldInsn (GETSTATIC , Type .getInternalName (wrapper .getDeclaringClass ()),
279- "TYPE" , "Ljava/lang/Class;" );
280- } else {
281- visitor .visitLdcInsn (typeDef .toJvmType ());
282- }
283- }
284-
285- Method wrapper = Primitives .wrapper (value .getClass ());
286- if (wrapper != null ) {
287- visitor .visitMethodInsn (INVOKESTATIC , Type .getInternalName (wrapper .getDeclaringClass ()),
288- wrapper .getName (), Type .getMethodDescriptor (wrapper ), false );
289- }
290- }
291-
292- private void annotationBoolean (MethodVisitor visitor , Boolean value , boolean checkRange ,
293- Integer ... constants ) {
294- annotationPrimitive (visitor , value , checkRange ,
295- b -> b .booleanValue () ? 1 : 0 , constants );
296- }
297-
298- private void annotationCharacter (MethodVisitor visitor , Character value , boolean checkRange ,
299- Integer ... constants ) {
300- annotationPrimitive (visitor , value , checkRange ,
301- c -> (int ) c .charValue (), constants );
302- }
303-
304- private void annotationNumber (MethodVisitor visitor , Number value , boolean checkRange ,
305- Integer ... constants ) {
306- annotationPrimitive (visitor , value , checkRange , Number ::intValue , constants );
307- }
308-
309- private <T > void annotationPrimitive (MethodVisitor visitor , T value ,
310- boolean checkRange , SneakyThrows .Function <T , Integer > intMapper , Integer ... constants ) {
311- int v = intMapper .apply (value ).intValue ();
312- if (v >= 0 && v <= constants .length ) {
313- visitor .visitInsn (constants [v ].intValue ());
314- } else {
315- if (checkRange ) {
316- if (v >= Byte .MIN_VALUE && v <= Byte .MAX_VALUE ) {
317- visitor .visitIntInsn (Opcodes .BIPUSH , v );
318- } else if (v >= Short .MIN_VALUE && v <= Short .MAX_VALUE ) {
319- visitor .visitIntInsn (Opcodes .SIPUSH , v );
320- } else {
321- visitor .visitLdcInsn (value );
322- }
323- } else {
324- visitor .visitLdcInsn (value );
325- }
326- }
327- }
328-
329131 private void setReturnType (MethodVisitor visitor , HandlerCompiler handler )
330132 throws NoSuchMethodException {
331133 TypeDefinition returnType = handler .getReturnType ();
0 commit comments