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