1 /*
2  * Copyright (C) 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 
17 package com.android.settingslib.search;
18 
19 import com.squareup.javapoet.ClassName;
20 import com.squareup.javapoet.FieldSpec;
21 import com.squareup.javapoet.JavaFile;
22 import com.squareup.javapoet.MethodSpec;
23 import com.squareup.javapoet.ParameterizedTypeName;
24 import com.squareup.javapoet.TypeName;
25 import com.squareup.javapoet.TypeSpec;
26 
27 import java.io.IOException;
28 import java.util.Collection;
29 import java.util.HashSet;
30 import java.util.Set;
31 
32 import javax.annotation.processing.AbstractProcessor;
33 import javax.annotation.processing.Filer;
34 import javax.annotation.processing.Messager;
35 import javax.annotation.processing.ProcessingEnvironment;
36 import javax.annotation.processing.RoundEnvironment;
37 import javax.annotation.processing.SupportedAnnotationTypes;
38 import javax.annotation.processing.SupportedSourceVersion;
39 import javax.lang.model.SourceVersion;
40 import javax.lang.model.element.Element;
41 import javax.lang.model.element.Modifier;
42 import javax.lang.model.element.Name;
43 import javax.lang.model.element.TypeElement;
44 import javax.lang.model.util.SimpleElementVisitor8;
45 import javax.tools.Diagnostic.Kind;
46 
47 /**
48  * Annotation processor for {@link SearchIndexable} that generates {@link SearchIndexableResources}
49  * subclasses.
50  */
51 @SupportedSourceVersion(SourceVersion.RELEASE_8)
52 @SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"})
53 public class IndexableProcessor extends AbstractProcessor {
54 
55     private static final String PACKAGE = "com.android.settingslib.search";
56     private static final String CLASS_BASE = "SearchIndexableResourcesBase";
57     private static final String CLASS_MOBILE = "SearchIndexableResourcesMobile";
58     private static final String CLASS_TV = "SearchIndexableResourcesTv";
59     private static final String CLASS_WEAR = "SearchIndexableResourcesWear";
60     private static final String CLASS_AUTO = "SearchIndexableResourcesAuto";
61     private static final String CLASS_ARC = "SearchIndexableResourcesArc";
62 
63     private Filer mFiler;
64     private Messager mMessager;
65     private boolean mRanOnce;
66 
67     @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment)68     public boolean process(Set<? extends TypeElement> annotations,
69             RoundEnvironment roundEnvironment) {
70         if (mRanOnce) {
71             // Will get called once per round, but we only want to run on the first one.
72             return true;
73         }
74         mRanOnce = true;
75 
76         final FieldSpec providers = FieldSpec.builder(
77                 ParameterizedTypeName.get(
78                         ClassName.get(Set.class),
79                         TypeName.get(Class.class)),
80                 "mProviders",
81                 Modifier.PRIVATE, Modifier.FINAL)
82                 .initializer("new $T()", HashSet.class)
83                 .build();
84 
85         final MethodSpec addIndex = MethodSpec.methodBuilder("addIndex")
86                 .addModifiers(Modifier.PUBLIC)
87                 .addParameter(ClassName.get(Class.class), "indexClass")
88                 .addCode("$N.add(indexClass);\n", providers)
89                 .build();
90 
91         final MethodSpec.Builder baseConstructorBuilder = MethodSpec.constructorBuilder()
92                 .addModifiers(Modifier.PUBLIC);
93         final MethodSpec.Builder mobileConstructorBuilder = MethodSpec.constructorBuilder()
94                 .addModifiers(Modifier.PUBLIC);
95         final MethodSpec.Builder tvConstructorBuilder = MethodSpec.constructorBuilder()
96                 .addModifiers(Modifier.PUBLIC);
97         final MethodSpec.Builder wearConstructorBuilder = MethodSpec.constructorBuilder()
98                 .addModifiers(Modifier.PUBLIC);
99         final MethodSpec.Builder autoConstructorBuilder = MethodSpec.constructorBuilder()
100                 .addModifiers(Modifier.PUBLIC);
101         final MethodSpec.Builder arcConstructorBuilder = MethodSpec.constructorBuilder()
102                 .addModifiers(Modifier.PUBLIC);
103 
104         for (Element element : roundEnvironment.getElementsAnnotatedWith(SearchIndexable.class)) {
105             if (element.getKind().isClass()) {
106                 Name className = element.accept(new SimpleElementVisitor8<Name, Void>() {
107                     @Override
108                     public Name visitType(TypeElement typeElement, Void aVoid) {
109                         return typeElement.getQualifiedName();
110                     }
111                 }, null);
112                 if (className != null) {
113                     SearchIndexable searchIndexable = element.getAnnotation(SearchIndexable.class);
114 
115                     int forTarget = searchIndexable.forTarget();
116                     if (forTarget == SearchIndexable.ALL) {
117                         baseConstructorBuilder.addCode("$N($L.class);\n", addIndex, className);
118                     } else if ((forTarget & SearchIndexable.MOBILE) != 0) {
119                         mobileConstructorBuilder.addCode("$N($L.class);\n", addIndex, className);
120                     } else if ((forTarget & SearchIndexable.TV) != 0) {
121                         tvConstructorBuilder.addCode("$N($L.class);\n", addIndex, className);
122                     } else if ((forTarget & SearchIndexable.WEAR) != 0) {
123                         wearConstructorBuilder.addCode("$N($L.class);\n", addIndex, className);
124                     } else if ((forTarget & SearchIndexable.AUTO) != 0) {
125                         autoConstructorBuilder.addCode("$N($L.class);\n", addIndex, className);
126                     } else if ((forTarget & SearchIndexable.ARC) != 0) {
127                         arcConstructorBuilder.addCode("$N($L.class);\n", addIndex, className);
128                     }
129                 } else {
130                     throw new IllegalStateException("Null classname from " + element);
131                 }
132             }
133         }
134 
135         final MethodSpec getProviderValues = MethodSpec.methodBuilder("getProviderValues")
136                 .addAnnotation(Override.class)
137                 .addModifiers(Modifier.PUBLIC)
138                 .returns(ParameterizedTypeName.get(
139                         ClassName.get(Collection.class),
140                         TypeName.get(Class.class)))
141                 .addCode("return $N;\n", providers)
142                 .build();
143 
144         final TypeSpec baseClass = TypeSpec.classBuilder(CLASS_BASE)
145                 .addModifiers(Modifier.PUBLIC)
146                 .addSuperinterface(ClassName.get(SearchIndexableResources.class))
147                 .addField(providers)
148                 .addMethod(baseConstructorBuilder.build())
149                 .addMethod(addIndex)
150                 .addMethod(getProviderValues)
151                 .build();
152         final JavaFile searchIndexableResourcesBase = JavaFile.builder(PACKAGE, baseClass).build();
153 
154         final JavaFile searchIndexableResourcesMobile = JavaFile.builder(PACKAGE,
155                 TypeSpec.classBuilder(CLASS_MOBILE)
156                         .addModifiers(Modifier.PUBLIC)
157                         .superclass(ClassName.get(PACKAGE, baseClass.name))
158                         .addMethod(mobileConstructorBuilder.build())
159                         .build())
160                 .build();
161 
162         final JavaFile searchIndexableResourcesTv = JavaFile.builder(PACKAGE,
163                 TypeSpec.classBuilder(CLASS_TV)
164                         .addModifiers(Modifier.PUBLIC)
165                         .superclass(ClassName.get(PACKAGE, baseClass.name))
166                         .addMethod(tvConstructorBuilder.build())
167                         .build())
168                 .build();
169 
170         final JavaFile searchIndexableResourcesWear = JavaFile.builder(PACKAGE,
171                 TypeSpec.classBuilder(CLASS_WEAR)
172                         .addModifiers(Modifier.PUBLIC)
173                         .superclass(ClassName.get(PACKAGE, baseClass.name))
174                         .addMethod(wearConstructorBuilder.build())
175                         .build())
176                 .build();
177 
178         final JavaFile searchIndexableResourcesAuto = JavaFile.builder(PACKAGE,
179                 TypeSpec.classBuilder(CLASS_AUTO)
180                         .addModifiers(Modifier.PUBLIC)
181                         .superclass(ClassName.get(PACKAGE, baseClass.name))
182                         .addMethod(autoConstructorBuilder.build())
183                         .build())
184                 .build();
185 
186         final JavaFile searchIndexableResourcesArc = JavaFile.builder(PACKAGE,
187                 TypeSpec.classBuilder(CLASS_ARC)
188                         .addModifiers(Modifier.PUBLIC)
189                         .superclass(ClassName.get(PACKAGE, baseClass.name))
190                         .addMethod(arcConstructorBuilder.build())
191                         .build())
192                 .build();
193 
194         try {
195             searchIndexableResourcesBase.writeTo(mFiler);
196             searchIndexableResourcesMobile.writeTo(mFiler);
197             searchIndexableResourcesTv.writeTo(mFiler);
198             searchIndexableResourcesWear.writeTo(mFiler);
199             searchIndexableResourcesAuto.writeTo(mFiler);
200             searchIndexableResourcesArc.writeTo(mFiler);
201         } catch (IOException e) {
202             mMessager.printMessage(Kind.ERROR, "Error while writing file: " + e);
203         }
204         return true;
205     }
206 
207     @Override
init(ProcessingEnvironment processingEnvironment)208     public synchronized void init(ProcessingEnvironment processingEnvironment) {
209         super.init(processingEnvironment);
210         mFiler = processingEnvironment.getFiler();
211         mMessager = processingEnvironment.getMessager();
212     }
213 }
214