Skip to content

Commit ef59854

Browse files
committed
Support reading annotations from controller interfaces
1 parent 02d1769 commit ef59854

File tree

3 files changed

+100
-18
lines changed

3 files changed

+100
-18
lines changed

src/main/java/io/dinject/javalin/generator/ControllerReader.java

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import javax.lang.model.element.ElementKind;
99
import javax.lang.model.element.ExecutableElement;
1010
import javax.lang.model.element.TypeElement;
11+
import javax.lang.model.type.TypeMirror;
12+
import javax.lang.model.util.ElementFilter;
13+
import java.lang.annotation.Annotation;
1114
import java.util.ArrayList;
1215
import java.util.List;
1316
import java.util.Set;
@@ -24,6 +27,10 @@ class ControllerReader {
2427

2528
private final TypeElement beanType;
2629

30+
private final List<Element> interfaces;
31+
32+
private final List<ExecutableElement> interfaceMethods;
33+
2734
private final List<String> roles;
2835

2936
private final List<MethodReader> methods = new ArrayList<>();
@@ -42,27 +49,85 @@ class ControllerReader {
4249
ControllerReader(TypeElement beanType, ProcessingContext ctx) {
4350
this.beanType = beanType;
4451
this.ctx = ctx;
52+
this.interfaces = initInterfaces();
53+
this.interfaceMethods = initInterfaceMethods();
4554
this.roles = Util.findRoles(beanType);
46-
this.produces = produces(beanType);
4755
if (ctx.isGeneratedAvailable()) {
4856
importTypes.add(Constants.GENERATED);
4957
}
5058
if (ctx.isOpenApiAvailable()) {
51-
docHidden = isDocHidden(beanType);
59+
docHidden = initDocHidden();
5260
}
5361
importTypes.add(Constants.SINGLETON);
5462
importTypes.add(Constants.API_BUILDER);
5563
importTypes.add(Constants.IMPORT_CONTROLLER);
5664
importTypes.add(beanType.getQualifiedName().toString());
65+
66+
this.produces = initProduces();
67+
}
68+
69+
private List<Element> initInterfaces() {
70+
71+
List<Element> interfaces = new ArrayList<>();
72+
73+
for (TypeMirror anInterface : beanType.getInterfaces()) {
74+
final Element ifaceElement = ctx.asElement(anInterface);
75+
if (ifaceElement.getAnnotation(Path.class) != null) {
76+
interfaces.add(ifaceElement);
77+
}
78+
}
79+
return interfaces;
80+
}
81+
82+
private List<ExecutableElement> initInterfaceMethods() {
83+
84+
List<ExecutableElement> ifaceMethods = new ArrayList<>();
85+
for (Element anInterface : interfaces) {
86+
ifaceMethods.addAll(ElementFilter.methodsIn(anInterface.getEnclosedElements()));
87+
}
88+
return ifaceMethods;
89+
}
90+
91+
<A extends Annotation> A findAnnotation(Class<A> type) {
92+
A annotation = beanType.getAnnotation(type);
93+
if (annotation != null) {
94+
return annotation;
95+
}
96+
for (Element anInterface : interfaces) {
97+
annotation = anInterface.getAnnotation(type);
98+
if (annotation != null) {
99+
return annotation;
100+
}
101+
}
102+
return null;
103+
}
104+
105+
<A extends Annotation> A findMethodAnnotation(Class<A> type, ExecutableElement element) {
106+
107+
for (ExecutableElement interfaceMethod : interfaceMethods) {
108+
if (matchMethod(interfaceMethod, element)) {
109+
final A annotation = interfaceMethod.getAnnotation(type);
110+
if (annotation != null) {
111+
ctx.logDebug("found interface method annotation : " + annotation + " 2:" + interfaceMethod);
112+
return annotation;
113+
}
114+
}
115+
}
116+
117+
return null;
118+
}
119+
120+
private boolean matchMethod(ExecutableElement interfaceMethod, ExecutableElement element) {
121+
return interfaceMethod.toString().equals(element.toString());
57122
}
58123

59-
private String produces(TypeElement beanType) {
60-
final Produces produces = beanType.getAnnotation(Produces.class);
124+
private String initProduces() {
125+
final Produces produces = findAnnotation(Produces.class);
61126
return (produces == null) ? null : produces.value();
62127
}
63128

64-
private boolean isDocHidden(TypeElement beanType) {
65-
return beanType.getAnnotation(Hidden.class) != null;
129+
private boolean initDocHidden() {
130+
return findAnnotation(Hidden.class) != null;
66131
}
67132

68133
String getProduces() {
@@ -110,7 +175,7 @@ List<MethodReader> getMethods() {
110175
}
111176

112177
String getPath() {
113-
Path path = beanType.getAnnotation(Path.class);
178+
Path path = findAnnotation(Path.class);
114179
if (path == null) {
115180
return null;
116181
}

src/main/java/io/dinject/javalin/generator/MethodReader.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import javax.lang.model.element.VariableElement;
2121
import javax.lang.model.type.TypeKind;
2222
import javax.lang.model.type.TypeMirror;
23+
import java.lang.annotation.Annotation;
2324
import java.util.ArrayList;
2425
import java.util.List;
2526

@@ -61,16 +62,25 @@ class MethodReader {
6162
this.isVoid = element.getReturnType().toString().equals("void");
6263
this.methodRoles = Util.findRoles(element);
6364
this.javadoc = Javadoc.parse(ctx.getDocComment(element));
64-
this.produces = produces(element, bean);
65+
this.produces = produces(bean);
6566

6667
readMethodAnnotation();
6768
}
6869

69-
private String produces(ExecutableElement element, ControllerReader bean) {
70-
final Produces produces = element.getAnnotation(Produces.class);
70+
private String produces(ControllerReader bean) {
71+
final Produces produces = findAnnotation(Produces.class);
7172
return (produces != null) ? produces.value() : bean.getProduces();
7273
}
7374

75+
<A extends Annotation> A findAnnotation(Class<A> type) {
76+
A annotation = element.getAnnotation(type);
77+
if (annotation != null) {
78+
return annotation;
79+
}
80+
81+
return bean.findMethodAnnotation(type, element);
82+
}
83+
7484
void read() {
7585
if (!methodRoles.isEmpty()) {
7686
bean.addStaticImportType(JAVALIN_ROLES);
@@ -109,7 +119,7 @@ void addMeta(ProcessingContext ctx) {
109119

110120
if (javadoc.isDeprecated()) {
111121
operation.setDeprecated(true);
112-
} else if (element.getAnnotation(Deprecated.class) != null) {
122+
} else if (findAnnotation(Deprecated.class) != null) {
113123
operation.setDeprecated(true);
114124
}
115125

@@ -157,7 +167,7 @@ private boolean isNoResponse(TypeMirror returnType) {
157167
* Return true if the method is included in documentation.
158168
*/
159169
private boolean notHidden() {
160-
return !ctx.isOpenApiAvailable() || element.getAnnotation(Hidden.class) == null;
170+
return !ctx.isOpenApiAvailable() || findAnnotation(Hidden.class) == null;
161171
}
162172

163173
void addRoute(Append writer) {
@@ -240,28 +250,28 @@ private boolean isReturnContent() {
240250

241251
private boolean readMethodAnnotation() {
242252

243-
Form form = element.getAnnotation(Form.class);
253+
Form form = findAnnotation(Form.class);
244254
if (form != null) {
245255
this.formMarker = true;
246256
}
247257

248-
Get get = element.getAnnotation(Get.class);
258+
Get get = findAnnotation(Get.class);
249259
if (get != null) {
250260
return setWebMethod(WebMethod.GET, get.value());
251261
}
252-
Put put = element.getAnnotation(Put.class);
262+
Put put = findAnnotation(Put.class);
253263
if (put != null) {
254264
return setWebMethod(WebMethod.PUT, put.value());
255265
}
256-
Post post = element.getAnnotation(Post.class);
266+
Post post = findAnnotation(Post.class);
257267
if (post != null) {
258268
return setWebMethod(WebMethod.POST, post.value());
259269
}
260-
Patch patch = element.getAnnotation(Patch.class);
270+
Patch patch = findAnnotation(Patch.class);
261271
if (patch != null) {
262272
return setWebMethod(WebMethod.PATCH, patch.value());
263273
}
264-
Delete delete = element.getAnnotation(Delete.class);
274+
Delete delete = findAnnotation(Delete.class);
265275
if (delete != null) {
266276
return setWebMethod(WebMethod.DELETE, delete.value());
267277
}

src/main/java/io/dinject/javalin/generator/ProcessingContext.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import javax.lang.model.element.TypeElement;
1313
import javax.lang.model.type.TypeMirror;
1414
import javax.lang.model.util.Elements;
15+
import javax.lang.model.util.Types;
1516
import javax.tools.Diagnostic;
1617
import javax.tools.FileObject;
1718
import javax.tools.JavaFileObject;
@@ -26,6 +27,7 @@ class ProcessingContext {
2627
private final Messager messager;
2728
private final Filer filer;
2829
private final Elements elements;
30+
private final Types types;
2931
private final SchemaBuilder schemaBuilder;
3032
private final boolean generatedAvailable;
3133
private final boolean openApiAvailable;
@@ -36,6 +38,7 @@ class ProcessingContext {
3638
this.messager = env.getMessager();
3739
this.filer = env.getFiler();
3840
this.elements = env.getElementUtils();
41+
this.types = env.getTypeUtils();
3942
this.schemaBuilder = new SchemaBuilder(env.getTypeUtils(), elements);
4043
this.generatedAvailable = isTypeAvailable(GENERATED);
4144
this.openApiAvailable = isTypeAvailable(OPENAPIDEFINITION);
@@ -100,4 +103,8 @@ Schema toSchema(TypeMirror asType) {
100103
Content createContent(TypeMirror returnType, String mediaType) {
101104
return schemaBuilder.createContent(returnType, mediaType);
102105
}
106+
107+
Element asElement(TypeMirror typeMirror) {
108+
return types.asElement(typeMirror);
109+
}
103110
}

0 commit comments

Comments
 (0)