package com.alibaba.android.arouter.compiler.processor;
import com.alibaba.android.arouter.compiler.utils.Consts;
import com.alibaba.android.arouter.compiler.utils.Logger;
import com.alibaba.android.arouter.compiler.utils.TypeUtils;
import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.enums.TypeKind;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import static com.alibaba.android.arouter.compiler.utils.Consts.ANNOTATION_TYPE_AUTOWIRED;
import static com.alibaba.android.arouter.compiler.utils.Consts.ISYRINGE;
import static com.alibaba.android.arouter.compiler.utils.Consts.JSON_SERVICE;
import static com.alibaba.android.arouter.compiler.utils.Consts.KEY_MODULE_NAME;
import static com.alibaba.android.arouter.compiler.utils.Consts.METHOD_INJECT;
import static com.alibaba.android.arouter.compiler.utils.Consts.NAME_OF_AUTOWIRED;
import static com.alibaba.android.arouter.compiler.utils.Consts.WARNING_TIPS;
import static javax.lang.model.element.Modifier.PUBLIC;
/**
* Processor used to create autowired helper
*
* @author zhilong <a href="mailto:zhilong.lzl@alibaba-inc.com">Contact me.</a>
* @version 1.0
* @since 2017/2/20 下午5:56
*/
@AutoService(Processor.class)//用 @AutoService 来注解这个处理器,可以自动生成配置信息。
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends AbstractProcessor {
private Filer mFiler; //文件相关的辅助类
private Logger logger;
private Types types; //类型相关的辅助类
private TypeUtils typeUtils;
private Elements elements; //元素相关的辅助类
private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>(); // 包含注解字段的Map集合 // Contain field need autowired and his super class.
private static final ClassName ARouterClass = ClassName.get("com.alibaba.android.arouter.launcher", "ARouter");
private static final ClassName AndroidLog = ClassName.get("android.util", "Log");
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnv.getFiler(); // Generate class.
types = processingEnv.getTypeUtils(); // Get type utils.
elements = processingEnv.getElementUtils(); // Get class meta.
typeUtils = new TypeUtils(types, elements);
logger = new Logger(processingEnv.getMessager()); // Package the log utils.
logger.info(">>> AutowiredProcessor init. <<<");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (CollectionUtils.isNotEmpty(set)) {
try {
logger.info(">>> Found autowired field, start... <<<");
//传入被Autowire注解修饰的集合并且执行分类填充进parentAndChild的Map集合
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
//执行解析
generateHelper();
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void generateHelper() throws IOException, IllegalAccessException {
TypeElement type_ISyringe = elements.getTypeElement(ISYRINGE); //获取注入对象的元素类
TypeElement type_JsonService = elements.getTypeElement(JSON_SERVICE);// 获取JsonService元素类
TypeMirror iProvider = elements.getTypeElement(Consts.IPROVIDER).asType(); //获取IProvider类对象
TypeMirror activityTm = elements.getTypeElement(Consts.ACTIVITY).asType(); //获取activity类对象
TypeMirror fragmentTm = elements.getTypeElement(Consts.FRAGMENT).asType(); //获取fragment类对象
TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType(); //获取fragment的v4版本的类对象
// Build input param name.
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build(); //生成方法的参数 ***(Object target)
if (MapUtils.isNotEmpty(parentAndChild)) { //解析parentAndChild
for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) { // 被autowire注解的循环对象
// Build method : 'inject'
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
//生成方法injtct 所以和上面联合在一块就是生成了@override public void inject(Object target)
TypeElement parent = entry.getKey();
List<Element> childs = entry.getValue();
String qualifiedName = parent.getQualifiedName().toString();//整个类名
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));//截取包名
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;//文件名
logger.info(">>> Start process " + childs.size() + " field in " + parent.getSimpleName() + " ... <<<");
// 生成类 类名是 **$$**$***实现ISyringe接口并且是public的类
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_ISyringe))
.addModifiers(PUBLIC);
// 生成一个私有的全局对象serializationService
FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
helper.addField(jsonServiceField);
// 在方法体内加入指定的语句执行
injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));
//生成方法体并且注入
// Generate method body, start inject.
for (Element element : childs) {
Autowired fieldConfig = element.getAnnotation(Autowired.class);
String fieldName = element.getSimpleName().toString();
if (types.isSubtype(element.asType(), iProvider)) { // It's provider //如果是iProvider的子类
if ("".equals(fieldConfig.name())) { // User has not set service path, then use byType.
//默认通过类解析
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
ARouterClass,
ClassName.get(element.asType())
);
} else { // use byName //如果指定了Autowired的name的话 则用name进行解析
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation();",
ClassName.get(element.asType()),
ARouterClass,
fieldConfig.name()
);
}
// Validater 校验
if (fieldConfig.required()) { //校验此字段是不是必填的不许为空
injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
injectMethodBuilder.addStatement(
"throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
} else { // It's normal intent value 如果是普通的参数传递
String originalValue = "substitute." + fieldName;
String statement = "substitute." + fieldName + " = substitute.";
boolean isActivity = false;
if (types.isSubtype(parent.asType(), activityTm)) { // Activity, then use getIntent() 如果是activity则通过getIntent注入
isActivity = true;
statement += "getIntent().";
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) { // Fragment, then use getArguments() 如果是fragment则通过getArguements进行注入
statement += "getArguments().";
} else {//其他方式暂不支持
throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
}
//判断类型进行相应的转换生成代码
statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity);
// 生成servicejson解析的服务循环解析生成代码
if (statement.startsWith("serializationService.")) { // Not mortals
injectMethodBuilder.beginControlFlow("if (null != serializationService)");
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = " + statement,
(StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
ClassName.get(element.asType())
);
injectMethodBuilder.nextControlFlow("else");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
} else {
//在方法体内加入代码
injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
}
// Validator 校验支持解析的类型为空则做记录
if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) { // Primitive wont be check.
injectMethodBuilder.beginControlFlow("if (null == substitute." + fieldName + ")");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
}
}
// 生成指定方法
helper.addMethod(injectMethodBuilder.build());
// Generate autowire helper 生成文件
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
logger.info(">>> " + parent.getSimpleName() + " has been processed, " + fileName + " has been generated. <<<");
}
logger.info(">>> Autowired processor stop. <<<");
}
}
private String buildStatement(String originalValue, String statement, int type, boolean isActivity) {
if (type == TypeKind.BOOLEAN.ordinal()) {
statement += (isActivity ? ("getBooleanExtra($S, " + originalValue + ")") : ("getBoolean($S)"));
} else if (type == TypeKind.BYTE.ordinal()) {
statement += (isActivity ? ("getByteExtra($S, " + originalValue + "") : ("getByte($S)"));
} else if (type == TypeKind.SHORT.ordinal()) {
statement += (isActivity ? ("getShortExtra($S, " + originalValue + ")") : ("getShort($S)"));
} else if (type == TypeKind.INT.ordinal()) {
statement += (isActivity ? ("getIntExtra($S, " + originalValue + ")") : ("getInt($S)"));
} else if (type == TypeKind.LONG.ordinal()) {
statement += (isActivity ? ("getLongExtra($S, " + originalValue + ")") : ("getLong($S)"));
} else if (type == TypeKind.CHAR.ordinal()) {
statement += (isActivity ? ("getCharExtra($S, " + originalValue + ")") : ("getChar($S)"));
} else if (type == TypeKind.FLOAT.ordinal()) {
statement += (isActivity ? ("getFloatExtra($S, " + originalValue + ")") : ("getFloat($S)"));
} else if (type == TypeKind.DOUBLE.ordinal()) {
statement += (isActivity ? ("getDoubleExtra($S, " + originalValue + ")") : ("getDouble($S)"));
} else if (type == TypeKind.STRING.ordinal()) {
statement += (isActivity ? ("getStringExtra($S)") : ("getString($S)"));
} else if (type == TypeKind.PARCELABLE.ordinal()) {
statement += (isActivity ? ("getParcelableExtra($S)") : ("getParcelable($S)"));
} else if (type == TypeKind.OBJECT.ordinal()) {
statement = "serializationService.parseObject(substitute." + (isActivity ? "getIntent()." : "getArguments().") + (isActivity ? "getStringExtra($S)" : "getString($S)") + ", new com.alibaba.android.arouter.facade.model.TypeWrapper<$T>(){}.getType())";
}
return statement;
}
/**
* Categories field, find his papa.
*
* @param elements Field need autowired
*/
private void categories(Set<? extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {//判断注解集合是不是为空
for (Element element : elements) {//循环解析
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();// 获取父元素
if (element.getModifiers().contains(Modifier.PRIVATE)) {// 检验是否注解的对象不能被private修饰
throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
}
if (parentAndChild.containsKey(enclosingElement)) { // Has categries
parentAndChild.get(enclosingElement).add(element);
} else {//第1次进入会生成列表并且组装到parentAndChild的map中
List<Element> childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
logger.info("categories finished.");
}
}
}