Android Open Source - injectextra Inject Extra Processor






From Project

Back to project page injectextra.

License

The source code is released under:

Apache License

If you think the Android project injectextra listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.github.stephanenicolas.injectextra;
//from w ww  .j a va2  s .  c om
import android.os.Bundle;
import android.os.Parcelable;
import com.github.stephanenicolas.afterburner.AfterBurner;
import com.github.stephanenicolas.afterburner.exception.AfterBurnerImpossibleException;
import com.github.stephanenicolas.morpheus.commons.NullableUtils;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;
import javassist.build.IClassTransformer;
import javassist.build.JavassistBuildException;
import lombok.extern.slf4j.Slf4j;
import static com.github.stephanenicolas.morpheus.commons.JavassistUtils.*;


import static java.lang.String.format;

/**
 * Injects all extras form intent inside an activity.
 *
 * @author SNI
 * @see {@link InjectExtra}
 */
@Slf4j
public class InjectExtraProcessor implements IClassTransformer {

  private AfterBurner afterBurner = new AfterBurner();

  @Override
  public boolean shouldTransform(CtClass candidateClass) throws JavassistBuildException {
    try {
      final List<CtField> fields =
          getAllInjectedFieldsForAnnotation(candidateClass, InjectExtra.class);

      if (fields.isEmpty()) {
        return false;
      }

      boolean isValid = isValidClass(candidateClass);

      log.debug(
          format("Class %s will get transformed: %b", candidateClass.getSimpleName(), isValid));
      return isValid;
    } catch (Exception e) {
      String message = format("Error while filtering class %s", candidateClass.getName());
      log.debug(message, e);
      throw new JavassistBuildException(message, e);
    }
  }

  @Override
  public void applyTransformations(final CtClass classToTransform) throws JavassistBuildException {
    // Actually you must test if it exists, but it's just an example...
    log.debug("Analyzing " + classToTransform.getSimpleName());

    try {
      final List<CtField> fields =
          getAllInjectedFieldsForAnnotation(classToTransform, InjectExtra.class);
      String injectExtraStatements = createInjectExtraStatements(fields, classToTransform);

      log.debug("Preliminary body (before insertion) :" + injectExtraStatements);
      injectStuff(classToTransform, injectExtraStatements);
    } catch (Exception e) {
      String message = format("Error while processing class %s", classToTransform.getName());
      log.debug(message, e);
      throw new JavassistBuildException(message, e);
    }
  }

  //extension point for new classes
  private void injectStuff(CtClass targetClazz, String body)
      throws CannotCompileException, AfterBurnerImpossibleException, NotFoundException,
      InjectExtraException {
    afterBurner.afterOverrideMethod(targetClazz, "onCreate", body);
  }

  private List<CtField> getAllInjectedFieldsForAnnotation(CtClass clazz,
      Class<? extends Annotation> annotationClazz) {
    List<CtField> result = new ArrayList<CtField>();
    CtField[] allFields = clazz.getDeclaredFields();
    log.debug("Scanning fields in " + clazz.getName());
    for (CtField field : allFields) {
      log.debug("Discovered field " + field.getName());
      if (field.hasAnnotation(annotationClazz)) {
        log.debug(
            "Field " + field.getName() + " has annotation " + annotationClazz.getSimpleName());
        result.add(field);
      }
    }
    return result;
  }

  private String createInjectExtraStatements(List<CtField> extrasToInject, CtClass targetClazz)
      throws ClassNotFoundException, NotFoundException {
    StringBuilder buffer = new StringBuilder('\n');
    for (CtField field : extrasToInject) {
      Object annotation = field.getAnnotation(InjectExtra.class);
      //must be accessed by introspection as I get a Proxy during tests.
      //this proxy comes from Robolectric
      Class annotionClass = annotation.getClass();

      //workaround for robolectric
      //https://github.com/robolectric/robolectric/pull/1240
      String value = null;
      boolean optional = false;
      try {
        Method method = annotionClass.getMethod("value");
        value = (String) method.invoke(annotation);
        method = annotionClass.getMethod("optional");
        Boolean optionalObject = (Boolean) method.invoke(annotation);
        if (optionalObject!= null && optionalObject) {
          optional = true;
        }
      } catch (Exception e) {
        log.debug("How did we get there ?", e);
      }

      String assignment = field.getName() + " = ";
      String fieldName = field.getName();
      String findExtraString = "";
      ClassPool classPool = targetClazz.getClassPool();
      //please note that default values when reading extras are not used.
      if (isSubClass(classPool, field.getType(), String.class)) {
        findExtraString = "getIntent().getStringExtra(\"" + value + "\")";
      } else if (isStringArray(field, classPool)) {
        findExtraString = "getIntent().getStringArrayExtra(\"" + value + "\")";
      } else if (isCharSequenceArray(field, classPool)) {
        findExtraString = "getIntent().getCharSequenceArrayExtra(\"" + value + "\")";
      } else if (isBooleanArray(field)) {
        findExtraString = "getIntent().getBooleanArrayExtra(\"" + value + "\")";
      } else if (isIntArray(field)) {
        findExtraString = "getIntent().getIntArrayExtra(\"" + value + "\")";
      } else if (isByteArray(field)) {
        findExtraString = "getIntent().getByteArrayExtra(\"" + value + "\")";
      } else if (isCharArray(field)) {
        findExtraString = "getIntent().getCharArrayExtra(\"" + value + "\")";
      } else if (isFloatArray(field)) {
        findExtraString = "getIntent().getFloatArrayExtra(\"" + value + "\")";
      } else if (isDoubleArray(field)) {
        findExtraString = "getIntent().getDoubleArrayExtra(\"" + value + "\")";
      } else if (isShortArray(field)) {
        findExtraString = "getIntent().getShortArrayExtra(\"" + value + "\")";
      } else if (isParcelableArray(field, classPool)) {
        findExtraString = "getIntent().getParcelableArrayExtra(\"" + value + "\")";
      } else if (isSubClass(classPool, field.getType(), Parcelable.class)) {
        findExtraString = "getIntent().getParcelableExtra(\"" + value + "\")";
      } else if (isSubClass(classPool, field.getType(), Serializable.class)) {
        findExtraString = "getIntent().getSerializableExtra(\"" + value + "\")";
      } else if (field.getType().subtypeOf(CtClass.intType)) {
        findExtraString = "getIntent().getIntExtra(\"" + value + "\", -1)";
      } else if (isSubClass(classPool, field.getType(), Integer.class)) {
        findExtraString = "new Integer(getIntent().getIntExtra(\"" + value + "\", -1))";
      } else if (isArrayList(field, classPool)) {
        findExtraString = "getIntent().getCharSequenceArrayListExtra(\"" + value + "\")";
      } else if (field.getType().subtypeOf(CtClass.booleanType)) {
        findExtraString = "getIntent().getBooleanExtra(\"" + value + "\", false)";
      } else if (isSubClass(classPool, field.getType(), Boolean.class)) {
        findExtraString = "new Boolean(getIntent().getBooleanExtra(\"" + value + "\", false))";
      } else if (isSubClass(classPool, field.getType(), Bundle.class)) {
        findExtraString = "getIntent().getBundleExtra(\"" + value + "\")";
      } else if (field.getType().subtypeOf(CtClass.byteType)) {
        findExtraString = "getIntent().getByteExtra(\"" + value + "\", (byte)-1)";
      } else if (isSubClass(classPool, field.getType(), Byte.class)) {
        findExtraString = "new Byte(getIntent().getByteExtra(\"" + value + "\", (byte)-1))";
      } else if (field.getType().subtypeOf(CtClass.charType)) {
        findExtraString = "getIntent().getCharExtra(\"" + value + "\", '\\u0000')";
      } else if (field.getType().subtypeOf(CtClass.floatType)) {
        findExtraString = "getIntent().getFloatExtra(\"" + value + "\", -1f)";
      } else if (isSubClass(classPool, field.getType(), Float.class)) {
        findExtraString = "new Float(getIntent().getFloatExtra(\"" + value + "\", (float)-1f))";
      } else if (field.getType().subtypeOf(CtClass.shortType)) {
        findExtraString = "getIntent().getShortExtra(\"" + value + "\", (short)1)";
      } else if (isSubClass(classPool, field.getType(), Short.class)) {
        findExtraString = "new Short(getIntent().getShortExtra(\"" + value + "\", (short)1))";
      } else if (field.getType().subtypeOf(CtClass.doubleType)) {
        findExtraString = "getIntent().getDoubleExtra(\"" + value + "\", 1.0)";
      } else if (isSubClass(classPool, field.getType(), Double.class)) {
        findExtraString = "new Double(getIntent().getDoubleExtra(\"" + value + "\", 1.0))";
      } else if (isSubClass(classPool, field.getType(), Character.class)) {
        findExtraString = "new Character(getIntent().getCharExtra(\"" + value + "\", '\\u0000'))";
      } else if (isSubClass(classPool, field.getType(), CharSequence.class)) {
        findExtraString = "getIntent().getCharSequenceExtra(\"" + value + "\")";
      } else {
        findExtraString = "(" +field.getType().getName()+ ")" + "getIntent().getExtras().get(\"" + value + "\")";
      }
      buffer.append(checkOptional(assignment, value, optional, findExtraString, fieldName));
      buffer.append("\n");
      buffer.append(checkNullable(field, fieldName));
      buffer.append("\n");
    }
    return buffer.toString();
  }

  private String checkNullable(CtField field, String fieldName) throws NotFoundException {
    String checkNullable = "";
    if (!field.getType().isPrimitive() && !NullableUtils.isNullable(field)) {
      checkNullable = "if ("
          + fieldName
          + " == null) {\n  throw new RuntimeException(\"Field "
          + fieldName
          + " is null and is not @Nullable.\"); \n}\n";
    }
    return checkNullable;
  }

  private String checkOptional(String fieldAssignment, String value, boolean optional,
      String findExtraString, String fieldName) {
    findExtraString = "if (getIntent().hasExtra(\""
        + value
        + "\")) {\n  "
        + fieldAssignment
        + findExtraString
        + ";\n}";
    if (!optional) {
      findExtraString = findExtraString
          + " else {\n  throw new RuntimeException(\"Field "
          + fieldName
          + " is not optional and is not present in extras.\");\n}";
    }
    return findExtraString;
  }

  private boolean isValidClass(CtClass clazz) throws NotFoundException {
    return isActivity(clazz);
  }

}




Java Source Code List

com.example.injectextra.MainActivity.java
com.example.injectextra.SecondActivity.java
com.github.stephanenicolas.injectextra.InjectExtraException.java
com.github.stephanenicolas.injectextra.InjectExtraProcessor.java
com.github.stephanenicolas.injectextra.InjectExtra.java