Skip to content

Commit 9063475

Browse files
committed
MVC Route: @ContextParam #1387
1 parent f8dd7f1 commit 9063475

13 files changed

Lines changed: 343 additions & 76 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.annotations;
7+
8+
import io.jooby.Context;
9+
10+
import java.lang.annotation.ElementType;
11+
import java.lang.annotation.Retention;
12+
import java.lang.annotation.RetentionPolicy;
13+
import java.lang.annotation.Target;
14+
15+
/**
16+
* Allow access to context attributes from MVC route.
17+
*
18+
* <pre>{@code
19+
* public String method(&#64;ContextParam String version) {
20+
* ...
21+
* }
22+
* }</pre>
23+
*
24+
* See {@link Context#getAttributes()}.
25+
*/
26+
@Retention(RetentionPolicy.RUNTIME)
27+
@Target(ElementType.PARAMETER)
28+
public @interface ContextParam {
29+
30+
/**
31+
* Attribute's name. See {@link io.jooby.Context#attribute(String)}
32+
*
33+
* @return Attribute's name.
34+
*/
35+
String value() default "";
36+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.annotations;
7+
8+
import io.jooby.Session;
9+
10+
import java.lang.annotation.ElementType;
11+
import java.lang.annotation.Retention;
12+
import java.lang.annotation.RetentionPolicy;
13+
import java.lang.annotation.Target;
14+
15+
/**
16+
* Allow access to session attributes from MVC route.
17+
*
18+
* <pre>{@code
19+
* public String method(&#64;SessionParam String userId) {
20+
* ...
21+
* }
22+
* }</pre>
23+
*
24+
* See {@link Session#toMap()}.
25+
*/
26+
@Retention(RetentionPolicy.RUNTIME)
27+
@Target(ElementType.PARAMETER)
28+
public @interface SessionParam {
29+
30+
/**
31+
* Session attribute's name.
32+
*
33+
* @return Session attribute's name.
34+
*/
35+
String value() default "";
36+
}

modules/jooby-apt/src/main/java/io/jooby/apt/Annotations.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import io.jooby.annotations.CONNECT;
99
import io.jooby.annotations.Consumes;
10+
import io.jooby.annotations.ContextParam;
1011
import io.jooby.annotations.CookieParam;
1112
import io.jooby.annotations.DELETE;
1213
import io.jooby.annotations.FlashParam;
@@ -82,6 +83,8 @@ public interface Annotations {
8283
*/
8384
Set<String> PATH_PARAMS = unmodifiableSet(new LinkedHashSet<>(asList(PathParam.class.getName())));
8485

86+
Set<String> CONTEXT_PARAMS = unmodifiableSet(new LinkedHashSet<>(asList(ContextParam.class.getName())));
87+
8588
/**
8689
* Query parameters.
8790
*/

modules/jooby-apt/src/main/java/io/jooby/internal/apt/ParamKind.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.jooby.Formdata;
1010
import io.jooby.Multipart;
1111
import io.jooby.apt.Annotations;
12+
import io.jooby.internal.apt.asm.ContextParamWriter;
1213
import io.jooby.internal.apt.asm.NamedParamWriter;
1314
import io.jooby.internal.apt.asm.BodyWriter;
1415
import io.jooby.internal.apt.asm.FileUploadWriter;
@@ -55,9 +56,28 @@ public enum ParamKind {
5556
}
5657

5758
@Override public ParamWriter newWriter() {
58-
return new NamedParamWriter(true);
59+
return new NamedParamWriter();
5960
}
6061
},
62+
63+
CONTEXT_PARAM {
64+
@Override public Set<String> annotations() {
65+
return Annotations.CONTEXT_PARAMS;
66+
}
67+
68+
@Override public Method valueObject(ParamDefinition param) throws NoSuchMethodException {
69+
return Context.class.getDeclaredMethod("getAttributes");
70+
}
71+
72+
@Override public Method singleValue(ParamDefinition param) throws NoSuchMethodException {
73+
return Context.class.getDeclaredMethod("attribute", String.class);
74+
}
75+
76+
@Override public ParamWriter newWriter() {
77+
return new ContextParamWriter();
78+
}
79+
},
80+
6181
QUERY_PARAM {
6282
@Override public Set<String> annotations() {
6383
return Annotations.QUERY_PARAMS;
@@ -72,7 +92,7 @@ public enum ParamKind {
7292
}
7393

7494
@Override public ParamWriter newWriter() {
75-
return new NamedParamWriter(true);
95+
return new NamedParamWriter();
7696
}
7797
},
7898
COOKIE_PARAM {
@@ -89,7 +109,7 @@ public enum ParamKind {
89109
}
90110

91111
@Override public ParamWriter newWriter() {
92-
return new NamedParamWriter(false);
112+
return new NamedParamWriter();
93113
}
94114
},
95115
HEADER_PARAM {
@@ -106,7 +126,7 @@ public enum ParamKind {
106126
}
107127

108128
@Override public ParamWriter newWriter() {
109-
return new NamedParamWriter(false);
129+
return new NamedParamWriter();
110130
}
111131
},
112132
FLASH_PARAM {
@@ -123,7 +143,7 @@ public enum ParamKind {
123143
}
124144

125145
@Override public ParamWriter newWriter() {
126-
return new NamedParamWriter(false);
146+
return new NamedParamWriter();
127147
}
128148
},
129149
FORM_PARAM {
@@ -140,7 +160,7 @@ public enum ParamKind {
140160
}
141161

142162
@Override public ParamWriter newWriter() {
143-
return new NamedParamWriter(true);
163+
return new NamedParamWriter();
144164
}
145165
},
146166
SESSION_PARAM {

modules/jooby-apt/src/main/java/io/jooby/internal/apt/Primitives.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,44 @@ public static Method wrapper(String name) throws NoSuchMethodException {
5050
return null;
5151
}
5252
}
53+
54+
public static Method toPrimitive(TypeDefinition type) throws NoSuchMethodException {
55+
return toPrimitive(type.getType());
56+
}
57+
58+
public static Method toPrimitive(TypeMirror type) throws NoSuchMethodException {
59+
return toPrimitive(type.getKind());
60+
}
61+
62+
public static Method toPrimitive(TypeKind type) throws NoSuchMethodException {
63+
return toPrimitive(type.name().toLowerCase());
64+
}
65+
66+
public static Method toPrimitive(Class type) throws NoSuchMethodException {
67+
return toPrimitive(type.getSimpleName());
68+
}
69+
70+
public static Method toPrimitive(String name) throws NoSuchMethodException {
71+
switch (name.toLowerCase()) {
72+
case "boolean":
73+
return Boolean.class.getDeclaredMethod("booleanValue");
74+
case "character":
75+
return Character.class.getDeclaredMethod("charValue");
76+
case "byte":
77+
return Byte.class.getDeclaredMethod("byteValue");
78+
case "short":
79+
return Short.class.getDeclaredMethod("shortValue");
80+
case "int":
81+
case "integer":
82+
return Integer.class.getDeclaredMethod("intValue");
83+
case "long":
84+
return Long.class.getDeclaredMethod("longValue");
85+
case "float":
86+
return Float.class.getDeclaredMethod("floatValue");
87+
case "double":
88+
return Double.class.getDeclaredMethod("doubleValue");
89+
default:
90+
return null;
91+
}
92+
}
5393
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.apt.asm;
7+
8+
import io.jooby.internal.apt.ParamDefinition;
9+
import io.jooby.internal.apt.Primitives;
10+
import org.objectweb.asm.ClassWriter;
11+
import org.objectweb.asm.Label;
12+
import org.objectweb.asm.MethodVisitor;
13+
import org.objectweb.asm.Opcodes;
14+
15+
import java.lang.reflect.Method;
16+
import java.util.Map;
17+
18+
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
19+
import static org.objectweb.asm.Opcodes.ACC_STATIC;
20+
import static org.objectweb.asm.Opcodes.ALOAD;
21+
import static org.objectweb.asm.Opcodes.ARETURN;
22+
import static org.objectweb.asm.Opcodes.ASTORE;
23+
import static org.objectweb.asm.Opcodes.F_APPEND;
24+
import static org.objectweb.asm.Opcodes.F_SAME1;
25+
import static org.objectweb.asm.Opcodes.GOTO;
26+
import static org.objectweb.asm.Opcodes.IFEQ;
27+
import static org.objectweb.asm.Opcodes.IFLE;
28+
import static org.objectweb.asm.Opcodes.IFNONNULL;
29+
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
30+
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
31+
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
32+
import static org.objectweb.asm.Type.getInternalName;
33+
import static org.objectweb.asm.Type.getMethodDescriptor;
34+
35+
public class ContextParamWriter extends ValueWriter {
36+
37+
@Override
38+
public void accept(ClassWriter writer, String handlerInternalName, MethodVisitor visitor,
39+
ParamDefinition parameter) throws Exception {
40+
String name = parameter.getHttpName();
41+
42+
visitor.visitLdcInsn(name);
43+
visitor.visitMethodInsn(INVOKESTATIC, handlerInternalName, parameter.getName(),
44+
"(Lio/jooby/Context;Ljava/lang/String;)Ljava/lang/Object;", false);
45+
if (parameter.getType().isPrimitive()) {
46+
Method toPrimitive = Primitives.toPrimitive(parameter.getType());
47+
visitor.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(toPrimitive.getDeclaringClass()));
48+
visitor.visitMethodInsn(INVOKEVIRTUAL, getInternalName(toPrimitive.getDeclaringClass()),
49+
toPrimitive.getName(), getMethodDescriptor(toPrimitive), false);
50+
} else {
51+
visitor.visitTypeInsn(Opcodes.CHECKCAST, parameter.getType().toJvmType().getInternalName());
52+
}
53+
54+
attribute(writer, parameter);
55+
}
56+
57+
private void attribute(ClassWriter classWriter, ParamDefinition parameter)
58+
throws NoSuchMethodException {
59+
MethodVisitor methodVisitor = classWriter
60+
.visitMethod(ACC_PRIVATE | ACC_STATIC, parameter.getName(),
61+
"(Lio/jooby/Context;Ljava/lang/String;)Ljava/lang/Object;",
62+
"<T:Ljava/lang/Object;>(Lio/jooby/Context;Ljava/lang/String;)TT;", null);
63+
methodVisitor.visitParameter("ctx", 0);
64+
methodVisitor.visitParameter("name", 0);
65+
methodVisitor.visitCode();
66+
methodVisitor.visitVarInsn(ALOAD, 0);
67+
methodVisitor.visitVarInsn(ALOAD, 1);
68+
Method attribute = parameter.getSingleValue();
69+
methodVisitor.visitMethodInsn(INVOKEINTERFACE, getInternalName(attribute.getDeclaringClass()),
70+
attribute.getName(), getMethodDescriptor(attribute), true);
71+
72+
// if (attribute == null)
73+
if (parameter.is(Map.class, String.class, Object.class)) {
74+
// return ctx.getAttributes();
75+
methodVisitor.visitVarInsn(ASTORE, 2);
76+
Label label1 = new Label();
77+
methodVisitor.visitLabel(label1);
78+
methodVisitor.visitVarInsn(ALOAD, 2);
79+
Label label2 = new Label();
80+
methodVisitor.visitJumpInsn(IFNONNULL, label2);
81+
Label label3 = new Label();
82+
methodVisitor.visitLabel(label3);
83+
methodVisitor.visitVarInsn(ALOAD, 0);
84+
Method attributes = parameter.getObjectValue();
85+
methodVisitor
86+
.visitMethodInsn(INVOKEINTERFACE, getInternalName(attributes.getDeclaringClass()),
87+
attributes.getName(),
88+
getMethodDescriptor(attributes), true);
89+
methodVisitor.visitInsn(ARETURN);
90+
methodVisitor.visitLabel(label2);
91+
methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"java/lang/Object"}, 0, null);
92+
methodVisitor.visitVarInsn(ALOAD, 2);
93+
}
94+
methodVisitor.visitInsn(ARETURN);
95+
96+
methodVisitor.visitMaxs(0, 0);
97+
methodVisitor.visitEnd();
98+
}
99+
}

modules/jooby-apt/src/main/java/io/jooby/internal/apt/asm/NamedParamWriter.java

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,15 @@
77

88
import io.jooby.internal.apt.ParamDefinition;
99
import org.objectweb.asm.ClassWriter;
10-
import org.objectweb.asm.Label;
1110
import org.objectweb.asm.MethodVisitor;
1211

1312
import java.lang.reflect.Method;
1413

15-
import static org.objectweb.asm.Opcodes.*;
1614
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
1715
import static org.objectweb.asm.Type.getMethodDescriptor;
1816

1917
public class NamedParamWriter extends ValueWriter {
2018

21-
private boolean tryName;
22-
23-
public NamedParamWriter(boolean tryName) {
24-
this.tryName = tryName;
25-
}
26-
2719
@Override
2820
public void accept(ClassWriter writer, String handlerInternalName, MethodVisitor visitor,
2921
ParamDefinition parameter) throws Exception {
@@ -43,41 +35,4 @@ public void accept(ClassWriter writer, String handlerInternalName, MethodVisitor
4335

4436
super.accept(writer, handlerInternalName, visitor, parameter);
4537
}
46-
47-
private void param(ClassWriter writer, ParamDefinition parameter) {
48-
MethodVisitor methodVisitor = writer.visitMethod(ACC_PRIVATE | ACC_STATIC, parameter.getName(),
49-
"(Lio/jooby/Value;Ljava/lang/String;)Lio/jooby/Value;", null, null);
50-
methodVisitor.visitParameter("scope", 0);
51-
methodVisitor.visitParameter("name", 0);
52-
methodVisitor.visitCode();
53-
Label label0 = new Label();
54-
methodVisitor.visitLabel(label0);
55-
methodVisitor.visitVarInsn(ALOAD, 0);
56-
methodVisitor.visitVarInsn(ALOAD, 1);
57-
methodVisitor.visitMethodInsn(INVOKEINTERFACE, "io/jooby/Value", "get",
58-
"(Ljava/lang/String;)Lio/jooby/Value;", true);
59-
methodVisitor.visitVarInsn(ASTORE, 2);
60-
Label label1 = new Label();
61-
methodVisitor.visitLabel(label1);
62-
methodVisitor.visitVarInsn(ALOAD, 2);
63-
methodVisitor.visitMethodInsn(INVOKEINTERFACE, "io/jooby/Value", "isMissing", "()Z", true);
64-
Label label2 = new Label();
65-
methodVisitor.visitJumpInsn(IFEQ, label2);
66-
methodVisitor.visitVarInsn(ALOAD, 0);
67-
methodVisitor.visitMethodInsn(INVOKEINTERFACE, "io/jooby/Value", "size", "()I", true);
68-
methodVisitor.visitJumpInsn(IFLE, label2);
69-
methodVisitor.visitVarInsn(ALOAD, 0);
70-
Label label3 = new Label();
71-
methodVisitor.visitJumpInsn(GOTO, label3);
72-
methodVisitor.visitLabel(label2);
73-
methodVisitor.visitFrame(F_APPEND,1, new Object[] {"io/jooby/Value"}, 0, null);
74-
methodVisitor.visitVarInsn(ALOAD, 2);
75-
methodVisitor.visitLabel(label3);
76-
methodVisitor.visitFrame(F_SAME1, 0, null, 1, new Object[] {"io/jooby/Value"});
77-
methodVisitor.visitInsn(ARETURN);
78-
Label label4 = new Label();
79-
methodVisitor.visitLabel(label4);
80-
methodVisitor.visitMaxs(0, 0);
81-
methodVisitor.visitEnd();
82-
}
8338
}

modules/jooby-apt/src/test/java/output/MyController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
public class MyController {
1212

1313
@GET
14-
public String doIt(@PathParam("p1") String p1) {
14+
public int doIt(@PathParam("p1") int p1) {
1515
return p1;
1616
}
1717
}

0 commit comments

Comments
 (0)