1616import java .util .List ;
1717import java .util .Map ;
1818import java .util .Set ;
19+ import java .util .function .Predicate ;
1920import java .util .stream .Collectors ;
2021import java .util .stream .Stream ;
2122
@@ -59,20 +60,14 @@ public class JoobyProcessor extends AbstractProcessor {
5960
6061 private ProcessingEnvironment processingEnv ;
6162
62- /**
63- * Route Data.
64- * {
65- * HTTP_METHOD: [method1, ..., methodN]
66- * }
67- */
68- private Map <TypeElement , Map <TypeElement , List <ExecutableElement >>> routeMap = new LinkedHashMap <>();
69-
7063 private boolean debug ;
7164 private boolean incremental ;
7265 private boolean services ;
73- private boolean extendedLooupOfSuperTypes ;
66+ private boolean extendedLookupOfSuperTypes ;
7467
7568 private int round ;
69+ private Map <TypeElement , String > modules = new LinkedHashMap <>();
70+ private Set <String > alreadyProcessed = new HashSet <>();
7671
7772 @ Override
7873 public Set <String > getSupportedOptions () {
@@ -110,73 +105,89 @@ public synchronized void init(ProcessingEnvironment processingEnvironment) {
110105 debug = Opts .boolOpt (processingEnv , Opts .OPT_DEBUG , false );
111106 incremental = Opts .boolOpt (processingEnv , Opts .OPT_INCREMENTAL , true );
112107 services = Opts .boolOpt (processingEnv , Opts .OPT_SERVICES , true );
113- extendedLooupOfSuperTypes = Opts .boolOpt (processingEnv , Opts .OPT_EXTENDED_LOOKUP_OF_SUPERTYPES , false );
108+ extendedLookupOfSuperTypes = Opts .boolOpt (processingEnv , Opts .OPT_EXTENDED_LOOKUP_OF_SUPERTYPES ,
109+ true );
114110
115111 debug ("Incremental annotation processing is turned %s." , incremental ? "ON" : "OFF" );
116112 debug ("Generation of service provider configuration is turned %s." , services ? "ON" : "OFF" );
117- debug ("Extended lookup of superTypes %s." , extendedLooupOfSuperTypes ? "ON" : "OFF" );
113+ debug ("Extended lookup of superTypes %s." , extendedLookupOfSuperTypes ? "ON" : "OFF" );
118114 }
119115
120116 @ Override
121117 public boolean process (Set <? extends TypeElement > annotations , RoundEnvironment roundEnv ) {
122118 try {
123119 debug ("Round #%s" , round ++);
124120 if (roundEnv .processingOver ()) {
121+ if (services ) {
122+ doServices (processingEnv .getFiler (), modules );
123+ }
124+ return false ;
125+ } else {
126+ Map <TypeElement , Map <TypeElement , List <ExecutableElement >>> routeMap = collectRoutes (
127+ annotations , roundEnv );
125128
126- build (processingEnv .getFiler ());
129+ Map <TypeElement , String > modules = build (processingEnv .getFiler (), classes (routeMap ), alreadyProcessed ::add );
130+ alreadyProcessed .addAll (modules .values ());
131+ this .modules .putAll (modules );
127132
128- return false ;
133+ return true ;
129134 }
135+ } catch (Exception x ) {
136+ throw SneakyThrows .propagate (x );
137+ }
138+ }
139+
140+ private Map <TypeElement , Map <TypeElement , List <ExecutableElement >>> collectRoutes (
141+ Set <? extends TypeElement > annotations , RoundEnvironment roundEnv ) {
142+ Map <TypeElement , Map <TypeElement , List <ExecutableElement >>> routeMap = new LinkedHashMap <>();
143+
144+ for (TypeElement annotation : annotations ) {
145+ Set <? extends Element > elements = roundEnv
146+ .getElementsAnnotatedWith (annotation );
130147
131148 /**
132- * Do MVC handler: per each mvc method we create a Route.Handler.
149+ * Add empty-subclass (edge case where you mark something with @Path and didn't add any
150+ * HTTP annotation.
133151 */
134- for (TypeElement annotation : annotations ) {
135- Set <? extends Element > elements = roundEnv
136- .getElementsAnnotatedWith (annotation );
137-
138- /**
139- * Add empty-subclass (edge case where you mark something with @Path and didn't add any
140- * HTTP annotation.
141- */
142- elements .stream ()
143- .filter (TypeElement .class ::isInstance )
144- .map (TypeElement .class ::cast )
145- .filter (type -> !type .getModifiers ().contains (Modifier .ABSTRACT ))
146- .forEach (e -> routeMap .computeIfAbsent (e , k -> new LinkedHashMap <>()));
147-
148- if (Annotations .HTTP_METHODS .contains (annotation .asType ().toString ())) {
149- Set <ExecutableElement > methods = elements .stream ()
150- .filter (ExecutableElement .class ::isInstance )
151- .map (ExecutableElement .class ::cast )
152- .collect (Collectors .toCollection (LinkedHashSet ::new ));
153- for (ExecutableElement method : methods ) {
154- Map <TypeElement , List <ExecutableElement >> mapping = routeMap
155- .computeIfAbsent ((TypeElement ) method .getEnclosingElement (),
156- k -> new LinkedHashMap <>());
157- mapping .computeIfAbsent (annotation , k -> new ArrayList <>()).add (method );
158- }
159- } else {
160- if (extendedLooupOfSuperTypes ) {
161- elements .stream ()
162- .filter (TypeElement .class ::isInstance )
163- .map (TypeElement .class ::cast )
164- .forEach (parentTypeElement -> extendedLookupOfSuperTypes (parentTypeElement ));
165- }
152+ elements .stream ()
153+ .filter (TypeElement .class ::isInstance )
154+ .map (TypeElement .class ::cast )
155+ .filter (type -> !type .getModifiers ().contains (Modifier .ABSTRACT ))
156+ .forEach (e -> routeMap .computeIfAbsent (e , k -> new LinkedHashMap <>()));
157+
158+ if (Annotations .HTTP_METHODS .contains (annotation .asType ().toString ())) {
159+ Set <ExecutableElement > methods = elements .stream ()
160+ .filter (ExecutableElement .class ::isInstance )
161+ .map (ExecutableElement .class ::cast )
162+ .collect (Collectors .toCollection (LinkedHashSet ::new ));
163+ for (ExecutableElement method : methods ) {
164+ Map <TypeElement , List <ExecutableElement >> mapping = routeMap
165+ .computeIfAbsent ((TypeElement ) method .getEnclosingElement (),
166+ k -> new LinkedHashMap <>());
167+ mapping .computeIfAbsent (annotation , k -> new ArrayList <>()).add (method );
168+ }
169+ } else {
170+ if (extendedLookupOfSuperTypes ) {
171+ elements .stream ()
172+ .filter (TypeElement .class ::isInstance )
173+ .map (TypeElement .class ::cast )
174+ .forEach (
175+ parentTypeElement -> extendedLookupOfSuperTypes (routeMap , parentTypeElement ));
166176 }
167177 }
168- return true ;
169- } catch (Exception x ) {
170- throw SneakyThrows .propagate (x );
171178 }
179+ return routeMap ;
172180 }
173181
174182 /**
175183 * Crawls through the sub-classes. Inspects them for HTTP Method annotated entries
176184 *
185+ * @param routeMap
177186 * @param parentTypeElement
178187 */
179- private void extendedLookupOfSuperTypes (TypeElement parentTypeElement ) {
188+ private void extendedLookupOfSuperTypes (
189+ Map <TypeElement , Map <TypeElement , List <ExecutableElement >>> routeMap ,
190+ TypeElement parentTypeElement ) {
180191 for (TypeElement superType : superTypes (parentTypeElement )) {
181192 //collect all declared methods
182193 Set <ExecutableElement > methods = superType .getEnclosedElements ().stream ()
@@ -198,7 +209,8 @@ private void extendedLookupOfSuperTypes(TypeElement parentTypeElement) {
198209 Map <TypeElement , List <ExecutableElement >> mapping = routeMap
199210 .computeIfAbsent (parentTypeElement ,
200211 k -> new LinkedHashMap <>());
201- List <ExecutableElement > list = mapping .computeIfAbsent (annotationType , k -> new ArrayList <>());
212+ List <ExecutableElement > list = mapping .computeIfAbsent (annotationType ,
213+ k -> new ArrayList <>());
202214 //ensure that the same method wasnt already defined in parent
203215 if (list .stream ().map (this ::signature ).noneMatch (signature (method )::equals )) {
204216 list .add (method );
@@ -209,8 +221,30 @@ private void extendedLookupOfSuperTypes(TypeElement parentTypeElement) {
209221 }
210222 }
211223
212- private void build (Filer filer ) throws Exception {
224+ private Map <TypeElement , String > build (Filer filer ,
225+ Map <TypeElement , List <HandlerCompiler >> classes , Predicate <String > includes ) throws Exception {
213226 Types typeUtils = processingEnv .getTypeUtils ();
227+
228+ Map <TypeElement , String > modules = new LinkedHashMap <>();
229+ for (Map .Entry <TypeElement , List <HandlerCompiler >> entry : classes .entrySet ()) {
230+ TypeElement type = entry .getKey ();
231+ String typeName = typeUtils .erasure (type .asType ()).toString ();
232+ if (includes .test (typeName )) {
233+ List <HandlerCompiler > handlers = entry .getValue ();
234+ ModuleCompiler module = new ModuleCompiler (processingEnv , typeName );
235+ String moduleClass = module .getModuleClass ();
236+ byte [] moduleBin = module .compile (handlers );
237+ onClass (moduleClass , moduleBin );
238+ writeClass (filer .createClassFile (moduleClass , type ), moduleBin );
239+ modules .put (type , moduleClass );
240+ }
241+ }
242+
243+ return modules ;
244+ }
245+
246+ private Map <TypeElement , List <HandlerCompiler >> classes (
247+ Map <TypeElement , Map <TypeElement , List <ExecutableElement >>> routeMap ) {
214248 Map <TypeElement , List <HandlerCompiler >> classes = new LinkedHashMap <>();
215249 for (Map .Entry <TypeElement , Map <TypeElement , List <ExecutableElement >>> e : routeMap
216250 .entrySet ()) {
@@ -255,24 +289,7 @@ private void build(Filer filer) throws Exception {
255289 }
256290 }
257291 }
258-
259- Map <TypeElement , String > modules = new LinkedHashMap <>();
260- for (Map .Entry <TypeElement , List <HandlerCompiler >> entry : classes .entrySet ()) {
261- TypeElement type = entry .getKey ();
262- String typeName = typeUtils .erasure (type .asType ()).toString ();
263- List <HandlerCompiler > handlers = entry .getValue ();
264- ModuleCompiler module = new ModuleCompiler (processingEnv , typeName );
265- String moduleClass = module .getModuleClass ();
266- byte [] moduleBin = module .compile (handlers );
267- onClass (moduleClass , moduleBin );
268- writeClass (filer .createClassFile (moduleClass , type ), moduleBin );
269-
270- modules .put (type , moduleClass );
271- }
272-
273- if (services ) {
274- doServices (filer , modules );
275- }
292+ return classes ;
276293 }
277294
278295 private String signature (ExecutableElement method ) {
@@ -307,9 +324,11 @@ private void debug(String format, Object... args) {
307324
308325 private void doServices (Filer filer , Map <TypeElement , String > modules ) throws IOException {
309326 String location = "META-INF/services/" + MvcFactory .class .getName ();
310- Element [] originatingElements = modules .keySet ().toArray (new Element [0 ]);
311327 debug ("%s" , location );
312- FileObject resource = filer .createResource (StandardLocation .CLASS_OUTPUT , "" , location , originatingElements );
328+
329+ Element [] originatingElements = modules .keySet ().toArray (new Element [0 ]);
330+ FileObject resource = filer .createResource (StandardLocation .CLASS_OUTPUT , "" , location ,
331+ originatingElements );
313332 StringBuilder content = new StringBuilder ();
314333 for (Map .Entry <TypeElement , String > e : modules .entrySet ()) {
315334 String classname = e .getValue ();
0 commit comments