Skip to content

Commit 0802a80

Browse files
committed
apt: route attributes add support for neste annotations
1 parent a3486c1 commit 0802a80

8 files changed

Lines changed: 329 additions & 222 deletions

File tree

jooby/src/main/java/io/jooby/Route.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,8 +637,8 @@ public Route(@Nonnull String method, @Nonnull String pattern, @Nonnull Handler h
637637
* @param name of the attribute to retrieve.
638638
* @return value of the specific attribute.
639639
*/
640-
public Object attribute(String name) {
641-
return attributes.get(name);
640+
public <T> T attribute(String name) {
641+
return (T) attributes.get(name);
642642
}
643643

644644
/**

modules/jooby-apt/src/main/java/io/jooby/internal/compiler/ModuleCompiler.java

Lines changed: 7 additions & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -11,54 +11,26 @@
1111
import io.jooby.MediaType;
1212
import io.jooby.Reified;
1313
import io.jooby.Route;
14-
import io.jooby.SneakyThrows;
1514
import org.objectweb.asm.ClassWriter;
1615
import org.objectweb.asm.Label;
1716
import org.objectweb.asm.MethodVisitor;
18-
import org.objectweb.asm.Opcodes;
1917
import org.objectweb.asm.Type;
2018

2119
import 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;
2820
import java.lang.reflect.Method;
29-
import java.util.ArrayList;
30-
import java.util.HashMap;
3121
import 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;
3823
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
3924
import static org.objectweb.asm.Opcodes.ACC_SUPER;
4025
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
4126
import static org.objectweb.asm.Opcodes.ALOAD;
4227
import static org.objectweb.asm.Opcodes.ASTORE;
43-
import static org.objectweb.asm.Opcodes.DCONST_0;
44-
import static org.objectweb.asm.Opcodes.DCONST_1;
4528
import 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;
4929
import static org.objectweb.asm.Opcodes.GETFIELD;
5030
import 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;
5731
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
5832
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
5933
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
60-
import static org.objectweb.asm.Opcodes.LCONST_0;
61-
import static org.objectweb.asm.Opcodes.LCONST_1;
6234
import static org.objectweb.asm.Opcodes.NEW;
6335
import static org.objectweb.asm.Opcodes.POP;
6436
import 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

Comments
 (0)