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