77
88import io .jooby .MvcFactory ;
99import io .jooby .SneakyThrows ;
10+ import io .jooby .annotations .Path ;
1011import io .jooby .internal .apt .HandlerCompiler ;
1112import io .jooby .internal .apt .ModuleCompiler ;
1213
1718import javax .lang .model .SourceVersion ;
1819import javax .lang .model .element .AnnotationMirror ;
1920import javax .lang .model .element .Element ;
21+ import javax .lang .model .element .ElementKind ;
2022import javax .lang .model .element .ExecutableElement ;
2123import javax .lang .model .element .TypeElement ;
24+ import javax .lang .model .type .DeclaredType ;
25+ import javax .lang .model .util .Elements ;
2226import javax .tools .FileObject ;
2327import javax .tools .JavaFileObject ;
2428import javax .tools .StandardLocation ;
2731import java .io .PrintWriter ;
2832import java .util .ArrayList ;
2933import java .util .Collections ;
34+ import java .util .HashMap ;
35+ import java .util .LinkedHashSet ;
3036import java .util .List ;
3137import java .util .Map ;
3238import java .util .Set ;
3339import java .util .stream .Collectors ;
3440import java .util .stream .Stream ;
3541
42+
3643/**
3744 * Jooby Annotation Processing Tool. It generates byte code for MVC routes.
3845 *
@@ -44,8 +51,24 @@ public class JoobyProcessor extends AbstractProcessor {
4451
4552 private List <String > moduleList = new ArrayList <>();
4653
54+ private Set <TypeElement > pathAnnotations ;
55+ private Set <TypeElement > httpAnnotations ;
56+
57+ final class MVCMethod {
58+ public ExecutableElement method ;
59+ public TypeElement httpMethod ;
60+
61+ MVCMethod (ExecutableElement method , TypeElement httpMethod ) {
62+ this .method = method ;
63+ this .httpMethod = httpMethod ;
64+ }
65+ }
66+
4767 @ Override public Set <String > getSupportedAnnotationTypes () {
48- return Annotations .HTTP_METHODS ;
68+ return new LinkedHashSet <String >() {{
69+ addAll (Annotations .HTTP_METHODS );
70+ addAll (Annotations .PATH );
71+ }};
4972 }
5073
5174 @ Override public SourceVersion getSupportedSourceVersion () {
@@ -54,6 +77,24 @@ public class JoobyProcessor extends AbstractProcessor {
5477
5578 @ Override public void init (ProcessingEnvironment processingEnvironment ) {
5679 this .processingEnvironment = processingEnvironment ;
80+
81+ Elements eltUtil = processingEnvironment .getElementUtils ();
82+ this .pathAnnotations = new LinkedHashSet <TypeElement >() {{
83+ for (String s : Annotations .PATH ) {
84+ TypeElement t = eltUtil .getTypeElement (s );
85+ if (t != null ) {
86+ add (t );
87+ }
88+ }
89+ }};
90+ this .httpAnnotations = new LinkedHashSet <TypeElement >() {{
91+ for (String s : Annotations .HTTP_METHODS ) {
92+ TypeElement t = eltUtil .getTypeElement (s );
93+ if (t != null ) {
94+ add (t );
95+ }
96+ }
97+ }};
5798 }
5899
59100 @ Override
@@ -63,22 +104,57 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
63104 doServices (processingEnvironment .getFiler ());
64105 return false ;
65106 }
107+
108+ JoobyProcessorRoundEnvironment joobyRoundEnv = new JoobyProcessorRoundEnvironment (roundEnv , processingEnvironment );
109+
110+ Map <TypeElement , List <MVCMethod >> classMap = new HashMap <>();
66111 /**
67112 * Do MVC handler: per each mvc method we create a Route.Handler.
68113 */
69114 List <HandlerCompiler > result = new ArrayList <>();
115+
116+ /**
117+ * If @Path annotation is present force inspecting all http mthods.
118+ */
119+ if (annotations .retainAll (this .pathAnnotations )) {
120+ annotations = httpAnnotations ;
121+ }
122+
70123 for (TypeElement httpMethod : annotations ) {
71- Set <? extends Element > methods = roundEnv .getElementsAnnotatedWith (httpMethod );
124+ Set <? extends Element > methods = joobyRoundEnv .getElementsAnnotatedWith (httpMethod );
72125 for (Element e : methods ) {
73126 ExecutableElement method = (ExecutableElement ) e ;
127+ TypeElement cls = (TypeElement ) method .getEnclosingElement ();
128+ TypeElement superCls = (TypeElement ) ((DeclaredType ) cls .getSuperclass ()).asElement ();
129+ superCls .getEnclosedElements ();
130+ if (!classMap .containsKey (cls )) {
131+ classMap .put (cls , new ArrayList <>());
132+ }
74133 List <String > paths = path (httpMethod , method );
75134 for (String path : paths ) {
76- HandlerCompiler compiler = new HandlerCompiler (processingEnvironment , method ,
77- httpMethod , path );
135+ HandlerCompiler compiler = new HandlerCompiler (processingEnvironment , method , httpMethod , path );
78136 result .add (compiler );
79137 }
138+ classMap .get (cls ).add (new MVCMethod (method , httpMethod ));
139+ }
140+ }
141+
142+ Set <? extends Element > pathAnnotatedElements = roundEnv .getElementsAnnotatedWith (Path .class );
143+ for (Element c : pathAnnotatedElements ) {
144+ if (c .getKind () == ElementKind .CLASS ) {
145+ TypeElement newOwner = (TypeElement ) c ;
146+ TypeElement oldOwner = (TypeElement ) ((DeclaredType ) newOwner .getSuperclass ()).asElement ();
147+ if (classMap .containsKey (oldOwner )) {
148+ for (MVCMethod e : classMap .get (oldOwner )) {
149+ for (String path : path (e .httpMethod , e .method , newOwner )) {
150+ HandlerCompiler compiler = new HandlerCompiler (processingEnvironment , e .method , newOwner , e .httpMethod , path );
151+ result .add (compiler );
152+ }
153+ }
154+ }
80155 }
81156 }
157+
82158 Filer filer = processingEnvironment .getFiler ();
83159 Map <String , List <HandlerCompiler >> classes = result .stream ()
84160 .collect (Collectors .groupingBy (e -> e .getController ().getName ()));
@@ -125,8 +201,8 @@ private void writeClass(JavaFileObject javaFileObject, byte[] bytecode) throws I
125201 }
126202 }
127203
128- private List <String > path (TypeElement method , ExecutableElement exec ) {
129- List <String > prefix = path (exec . getEnclosingElement () );
204+ private List <String > path (TypeElement method , ExecutableElement exec , TypeElement owner ) {
205+ List <String > prefix = path (owner );
130206 // Favor GET("/path") over Path("/path") at method level
131207 List <String > path = path (method .getQualifiedName ().toString (), method .getAnnotationMirrors ());
132208 if (path .size () == 0 ) {
@@ -145,6 +221,10 @@ private List<String> path(TypeElement method, ExecutableElement exec) {
145221 .collect (Collectors .toList ());
146222 }
147223
224+ private List <String > path (TypeElement method , ExecutableElement exec ) {
225+ return path (method , exec , (TypeElement ) exec .getEnclosingElement ());
226+ }
227+
148228 private List <String > path (Element element ) {
149229 return path (null , element .getAnnotationMirrors ());
150230 }
0 commit comments