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