package com.tale.prettybundleprocessor; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.tale.prettybundle.ExtraBinder; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.processing.Filer; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; /** * Created by tale on 2/5/15. */ public class ExtraUtilityClassBuilder { private static final String ACTIVITIES = "Activities"; private static final String FRAGMENTS = "Fragments"; private static final String SERVICES = "Services"; private static final String packageName = "com.tale.prettybundle"; private final Map<String, ExtraClassesGrouped> activityExtrasGroupedMap = new LinkedHashMap<String, ExtraClassesGrouped>(); private final Map<String, ExtraClassesGrouped> serviceExtrasGroupedMap = new LinkedHashMap<String, ExtraClassesGrouped>(); private final Map<String, ExtraClassesGrouped> fragmentExtrasGroupedMap = new LinkedHashMap<String, ExtraClassesGrouped>(); public ExtraUtilityClassBuilder() { } public void add(ExtraClassesGrouped extraClassesGrouped) { if (extraClassesGrouped == null || extraClassesGrouped.getExtraAnnotatedClassName() == null || extraClassesGrouped.getExtraAnnotatedClassName().trim().equals("")) { return; } final SupportedType supportedType = extraClassesGrouped.getSupportedType(); if (supportedType == SupportedType.ACTIVITY) { // We replace the existed with the new or just add. activityExtrasGroupedMap.put(extraClassesGrouped.getExtraAnnotatedClassName(), extraClassesGrouped); } else if (supportedType == SupportedType.SERVICE) { serviceExtrasGroupedMap.put(extraClassesGrouped.getExtraAnnotatedClassName(), extraClassesGrouped); } else if (supportedType == SupportedType.FRAGMENT) { fragmentExtrasGroupedMap.put(extraClassesGrouped.getExtraAnnotatedClassName(), extraClassesGrouped); } } public boolean contains(ExtraClassesGrouped extraClassesGrouped) { if (extraClassesGrouped == null || extraClassesGrouped.getExtraAnnotatedClassName() == null || extraClassesGrouped.getExtraAnnotatedClassName().trim().equals("")) { return false; } final SupportedType supportedType = extraClassesGrouped.getSupportedType(); if (supportedType == SupportedType.ACTIVITY) { return activityExtrasGroupedMap.containsKey(extraClassesGrouped.getExtraAnnotatedClassName()); } else if (supportedType == SupportedType.SERVICE) { return serviceExtrasGroupedMap.containsKey(extraClassesGrouped.getExtraAnnotatedClassName()); } else if (supportedType == SupportedType.FRAGMENT) { return fragmentExtrasGroupedMap.containsKey(extraClassesGrouped.getExtraAnnotatedClassName()); } return true; // We don't want to save not supported type. } public void generateCode(Elements elementUtils, Types typeUtils, Filer filer) throws IOException { if (activityExtrasGroupedMap.size() > 0) { generateCode(ACTIVITIES, activityExtrasGroupedMap, elementUtils, typeUtils, filer); } if (serviceExtrasGroupedMap.size() > 0) { generateCode(SERVICES, serviceExtrasGroupedMap, elementUtils, typeUtils, filer); } if (fragmentExtrasGroupedMap.size() > 0) { generateCode(FRAGMENTS, fragmentExtrasGroupedMap, elementUtils, typeUtils, filer); } } private void generateCode(String className, Map<String, ExtraClassesGrouped> extrasGroupedMap, Elements elementUtils, Types typeUtils, Filer filer) throws IOException { final TypeSpec.Builder activitiesClassBuilder = TypeSpec.classBuilder(className) .addModifiers(Modifier.PUBLIC); final List<MethodSpec> methods = getMethods(extrasGroupedMap, elementUtils, typeUtils); if (methods == null || methods.size() == 0) { // Nothing created if there is no @Extra annotation is Added. return; } for (MethodSpec method : methods) { activitiesClassBuilder.addMethod(method); } JavaFile.builder(packageName, activitiesClassBuilder.build()).build().writeTo(filer); } private List<MethodSpec> getMethods(Map<String, ExtraClassesGrouped> extrasGroupedMap, Elements elementUtils, Types typeUtils) { final int size = extrasGroupedMap.size(); if (size == 0) { return null; } final List<MethodSpec> methodSpecs = new ArrayList<MethodSpec>(size); for (ExtraClassesGrouped extraClassesGrouped : extrasGroupedMap.values()) { final MethodSpec methodSpec = createMethodSpec(elementUtils, typeUtils, extraClassesGrouped); if (methodSpec != null) { methodSpecs.add(methodSpec); } } return methodSpecs; } private MethodSpec createMethodSpec(Elements elementUtils, Types typeUtils, ExtraClassesGrouped extraClassesGrouped) { switch (extraClassesGrouped.getSupportedType()) { case ACTIVITY: case SERVICE: return createIntentMethodSpec(elementUtils, typeUtils, extraClassesGrouped); case FRAGMENT: return createFragmentMethodSpec(elementUtils, typeUtils, extraClassesGrouped); } return null; } private MethodSpec createIntentMethodSpec(Elements elementUtils, Types typeUtils, ExtraClassesGrouped extraClassesGrouped) { final String activityQualifiedClassName = extraClassesGrouped.getExtraAnnotatedClassName(); final TypeElement typeElement = elementUtils.getTypeElement(activityQualifiedClassName); final String activityName = typeElement.getSimpleName().toString(); // Declare method name. final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("create" + activityName + "Intent") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(Androids.intentClass()); // Build parameters. // Add Context object. methodSpecBuilder.addParameter(Androids.contextClass(), "context"); buildParameters(methodSpecBuilder, extraClassesGrouped); ClassName bundleClass = ClassName.get("android.os", "Bundle"); // Declare bundle object. methodSpecBuilder.addStatement("$T bundle = new $T()", bundleClass, bundleClass); // Put extras base on key, value to bundle. bindExtras(methodSpecBuilder, extraClassesGrouped, "bundle"); // Build and return Intent. return methodSpecBuilder.addStatement("$T intent = new $T(context, $L)", Androids.intentClass(), Androids.intentClass(), extraClassesGrouped.getExtraAnnotatedClassName() + ".class") .addStatement("intent.putExtras(bundle)") .addStatement("return intent") .build(); } private MethodSpec createFragmentMethodSpec(Elements elementUtils, Types typeUtils, ExtraClassesGrouped extraClassesGrouped) { final String fragmentQualifiedClassName = extraClassesGrouped.getExtraAnnotatedClassName(); final TypeElement typeElement = elementUtils.getTypeElement(fragmentQualifiedClassName); final String fragmentName = typeElement.getSimpleName().toString(); // Declare method name. final MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("create" + fragmentName) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(TypeName.get(elementUtils.getTypeElement(fragmentQualifiedClassName).asType())); // Build parameters. buildParameters(methodSpecBuilder, extraClassesGrouped); ClassName bundleClass = ClassName.get("android.os", "Bundle"); // Declare bundle object. methodSpecBuilder.addStatement("$T args = new $T()", bundleClass, bundleClass); // Put extras base on key, value to bundle. bindExtras(methodSpecBuilder, extraClassesGrouped, "args"); // Build and return Intent. return methodSpecBuilder.addStatement("$L fragment = new $L()", fragmentQualifiedClassName, fragmentQualifiedClassName) .addStatement("fragment.setArguments(args)") .addStatement("return fragment") .build(); } private void bindExtras(MethodSpec.Builder methodSpecBuilder, ExtraClassesGrouped extraClassesGrouped, String targetName) { final List<ExtraAnnotatedClass> extraAnnotatedClasses = extraClassesGrouped.getExtraAnnotatedClasses(); if (extraAnnotatedClasses == null || extraAnnotatedClasses.size() == 0) { return; } for (ExtraAnnotatedClass extraAnnotatedClass : extraAnnotatedClasses) { final ExtraBinder extraBinder = extraAnnotatedClass.getExtraBinder(); if (extraBinder == ExtraBinder.INTEGER || extraBinder == ExtraBinder.LONG || extraBinder == ExtraBinder.FLOAT || extraBinder == ExtraBinder.DOUBLE || extraBinder == ExtraBinder.BOOLEAN || extraBinder == ExtraBinder.BYTE || extraBinder == ExtraBinder.SHORT || extraBinder == ExtraBinder.CHAR ) { methodSpecBuilder.addStatement("$L.set($L, $S, $L)", ExtraBinder.class.getName() + "." + extraBinder.toString(), targetName, extraAnnotatedClass.getKey(), extraAnnotatedClass.getKey()); } else { methodSpecBuilder .beginControlFlow("if($L != null)", extraAnnotatedClass.getKey()) .addStatement("$L.set($L, $S, $L)", ExtraBinder.class.getName() + "." + extraBinder.toString(), targetName, extraAnnotatedClass.getKey(), extraAnnotatedClass.getKey()) .endControlFlow(); } } } private void buildParameters(MethodSpec.Builder methodSpecBuilder, ExtraClassesGrouped extraClassesGrouped) { final List<ExtraAnnotatedClass> extraAnnotatedClasses = extraClassesGrouped.getExtraAnnotatedClasses(); if (extraAnnotatedClasses == null || extraAnnotatedClasses.size() == 0) { return; } for (ExtraAnnotatedClass extraAnnotatedClass : extraAnnotatedClasses) { methodSpecBuilder.addParameter(TypeName.get(extraAnnotatedClass.getDataType()), extraAnnotatedClass.getKey()); } } public void clear() { activityExtrasGroupedMap.clear(); serviceExtrasGroupedMap.clear(); fragmentExtrasGroupedMap.clear(); } }