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