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