1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.remotecallback.compiler;
17 
18 import static androidx.remotecallback.compiler.RemoteCallbackProcessor.EXTERNAL_INPUT;
19 import static androidx.remotecallback.compiler.RemoteCallbackProcessor.REMOTE_CALLABLE;
20 
21 import org.jspecify.annotations.NonNull;
22 
23 import java.util.HashMap;
24 import java.util.Set;
25 
26 import javax.annotation.processing.AbstractProcessor;
27 import javax.annotation.processing.Messager;
28 import javax.annotation.processing.ProcessingEnvironment;
29 import javax.annotation.processing.RoundEnvironment;
30 import javax.annotation.processing.SupportedAnnotationTypes;
31 import javax.annotation.processing.SupportedSourceVersion;
32 import javax.lang.model.SourceVersion;
33 import javax.lang.model.element.Element;
34 import javax.lang.model.element.ElementKind;
35 import javax.lang.model.element.TypeElement;
36 
37 /**
38  * Processes annotations from RemoteCallbacks.
39  */
40 @SupportedAnnotationTypes({REMOTE_CALLABLE, EXTERNAL_INPUT})
41 @SupportedSourceVersion(SourceVersion.RELEASE_8)
42 public class RemoteCallbackProcessor extends AbstractProcessor {
43 
44     static final @NonNull String REMOTE_CALLABLE = "androidx.remotecallback.RemoteCallable";
45 
46     static final @NonNull String EXTERNAL_INPUT = "androidx.remotecallback.ExternalInput";
47 
48     private HashMap<Element, CallbackReceiver> mMap = new HashMap<>();
49     private ProcessingEnvironment mEnv;
50     private Messager mMessager;
51 
52     @Override
init(@onNull ProcessingEnvironment processingEnvironment)53     public synchronized void init(@NonNull ProcessingEnvironment processingEnvironment) {
54         mEnv = processingEnvironment;
55         mMessager = processingEnvironment.getMessager();
56     }
57 
58     @Override
process( @onNull Set<? extends TypeElement> set, @NonNull RoundEnvironment roundEnvironment )59     public boolean process(
60             @NonNull Set<? extends TypeElement> set,
61             @NonNull RoundEnvironment roundEnvironment
62     ) {
63         if (set.isEmpty()) return true;
64         TypeElement remoteCallable = findAnnotation(set, REMOTE_CALLABLE);
65 
66         for (Element element : roundEnvironment.getElementsAnnotatedWith(remoteCallable)) {
67             Element cls = findClass(element);
68             mMap.computeIfAbsent(cls, (c) -> new CallbackReceiver(c, mEnv, mMessager))
69                     .addMethod(element);
70         }
71         for (CallbackReceiver receiver: mMap.values()) {
72             receiver.finish(mEnv, mMessager);
73         }
74         return true;
75     }
76 
findClass(Element element)77     private Element findClass(Element element) {
78         if (element != null && element.getKind() != ElementKind.CLASS) {
79             return findClass(element.getEnclosingElement());
80         }
81         return element;
82     }
83 
findAnnotation(Set<? extends TypeElement> set, String name)84     private TypeElement findAnnotation(Set<? extends TypeElement> set, String name) {
85         for (TypeElement typeElement : set) {
86             if (String.valueOf(typeElement).equals(name)) {
87                 return typeElement;
88             }
89         }
90         return null;
91     }
92 }
93