package pocketknife.internal.codegen.binding;
import pocketknife.BindExtra;
import pocketknife.IntentSerializer;
import pocketknife.NotRequired;
import pocketknife.PocketKnifeIntentSerializer;
import pocketknife.internal.codegen.Access;
import pocketknife.internal.codegen.IntentFieldBinding;
import pocketknife.internal.codegen.InvalidTypeException;
import pocketknife.internal.codegen.KeySpec;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import static pocketknife.internal.GeneratedAdapters.INTENT_ADAPTER_SUFFIX;
public class IntentBindingProcessor extends BindingProcessor {
public IntentBindingProcessor(Messager messager, Elements elements, Types types) {
super(messager, elements, types);
}
public Map<TypeElement, IntentBindingAdapterGenerator> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, IntentBindingAdapterGenerator> targetClassMap = new LinkedHashMap<TypeElement, IntentBindingAdapterGenerator>();
Set<String> erasedTargetNames = new LinkedHashSet<String>();
// Process each @BindExtra
for (Element element : env.getElementsAnnotatedWith(BindExtra.class)) {
BindExtra annotation = element.getAnnotation(BindExtra.class);
if (annotation == null) {
continue;
}
try {
parseBindExtra(element, targetClassMap, erasedTargetNames);
} catch (Exception e) {
StringWriter stackTrace = new StringWriter();
e.printStackTrace(new PrintWriter(stackTrace));
error(element, "Unable to generate intent adapter for @BindExtra.\n\n%s", stackTrace);
}
}
// Try to find a parent adapter for each adapter
for (Map.Entry<TypeElement, IntentBindingAdapterGenerator> entry : targetClassMap.entrySet()) {
TypeElement parent = findParent(entry.getKey(), erasedTargetNames);
if (parent != null) {
entry.getValue().setParentAdapter(getPackageName(parent), parent.getSimpleName() + INTENT_ADAPTER_SUFFIX);
}
}
return targetClassMap;
}
private void parseBindExtra(Element element, Map<TypeElement, IntentBindingAdapterGenerator> targetClassMap, Set<String> erasedTargetNames)
throws InvalidTypeException {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// Verify that the target has all the appropriate information for type
TypeMirror type = element.asType();
if (type instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) type;
type = typeVariable.getUpperBound();
}
TypeMirror intentSerializer = getAnnotationElementClass(element, IntentSerializer.class);
validateSerializer(element, IntentSerializer.class, intentSerializer, PocketKnifeIntentSerializer.class);
validateNotRequiredArguments(element);
validateBindingPackage(BindExtra.class, element);
validateForCodeGeneration(BindExtra.class, element);
Access access = getAccess(BindExtra.class, element, enclosingElement);
// Assemble information on the bind point
String name = element.getSimpleName().toString();
String intentType = null;
KeySpec key = getKey(element);
boolean required = element.getAnnotation(NotRequired.class) == null;
boolean hasDefault = false;
boolean needsToBeCast = false;
if (intentSerializer == null) {
intentType = typeUtil.getIntentType(type);
hasDefault = typeUtil.isPrimitive(type);
needsToBeCast = typeUtil.needToCastIntentType(type);
}
IntentBindingAdapterGenerator intentBindingAdapterGenerator = getOrCreateTargetClass(targetClassMap, enclosingElement);
IntentFieldBinding binding = new IntentFieldBinding(name, access, type, intentType, key, needsToBeCast, hasDefault, required, intentSerializer);
intentBindingAdapterGenerator.addField(binding);
// Add the type-erased version to the valid targets set.
erasedTargetNames.add(enclosingElement.toString());
}
private KeySpec getKey(Element element) {
if (isDefaultAnnotationElement(element, BindExtra.class.getName(), "value")) {
return new KeySpec(null, generateKey(IntentFieldBinding.KEY_PREFIX, element.getSimpleName().toString()));
}
return new KeySpec(null, element.getAnnotation(BindExtra.class).value());
}
private IntentBindingAdapterGenerator getOrCreateTargetClass(Map<TypeElement, IntentBindingAdapterGenerator> targetClassMap,
TypeElement enclosingElement) {
IntentBindingAdapterGenerator intentBindingAdapterGenerator = targetClassMap.get(enclosingElement);
if (intentBindingAdapterGenerator == null) {
TypeMirror targetType = enclosingElement.asType();
String classPackage = getPackageName(enclosingElement);
String className = getClassName(enclosingElement, classPackage) + INTENT_ADAPTER_SUFFIX;
intentBindingAdapterGenerator = new IntentBindingAdapterGenerator(classPackage, className, targetType, typeUtil);
targetClassMap.put(enclosingElement, intentBindingAdapterGenerator);
}
return intentBindingAdapterGenerator;
}
}