package com.sora.util.akatsuki; import java.io.IOException; import java.util.EnumSet; import java.util.function.Function; import java.util.function.Predicate; import javax.annotation.processing.Filer; import com.sora.util.akatsuki.BundleRetainerClassBuilder.AnalysisTransformation; import com.sora.util.akatsuki.BundleRetainerClassBuilder.Direction; import com.sora.util.akatsuki.Retained.RestorePolicy; import com.sora.util.akatsuki.analyzers.CascadingTypeAnalyzer.Analysis; import com.sora.util.akatsuki.analyzers.Element; import com.sora.util.akatsuki.models.ClassInfo; import com.sora.util.akatsuki.models.FieldModel; import com.sora.util.akatsuki.models.SourceClassModel; import com.sora.util.akatsuki.models.SourceMappingModel; import com.sora.util.akatsuki.models.SourceTreeModel; import com.squareup.javapoet.JavaFile; public class RetainedStateModel extends SourceMappingModel implements AnalysisTransformation, Predicate<FieldModel> { private static final Function<ClassInfo, ClassInfo> CLASS_INFO_FUNCTION = info -> info .withNameTransform(Internal::generateRetainerClassName); private final ClassInfo info; private final RetainConfig config; RetainedStateModel(ProcessorContext context, SourceClassModel classModel, SourceTreeModel treeModel) { super(context, classModel, treeModel); this.info = CLASS_INFO_FUNCTION.apply(classModel().asClassInfo()); this.config = classModel().annotation(RetainConfig.class) .orElse(context.config().retainConfig()); } @Override public void writeToFile(Filer filer) throws IOException { if (!config.enabled()) { Log.verbose(context, "@Retained disabled for class " + classModel().asClassInfo() + ", skipping..."); return; } BundleRetainerClassBuilder builder = new BundleRetainerClassBuilder(context, classModel(), EnumSet.allOf(Direction.class), CLASS_INFO_FUNCTION, CLASS_INFO_FUNCTION); builder.withFieldPredicate(this); builder.withAnalysisTransformation(this); JavaFile javaFile = JavaFile .builder(info.fullyQualifiedPackageName, builder.build().build()).build(); javaFile.writeTo(filer); } @Override public ClassInfo classInfo() { return info; } @Override public void transform(ProcessorContext context, Direction direction, Element<?> element, Analysis analysis) { Retained retained = element.model().annotation(Retained.class) .orElseThrow(AssertionError::new); RestorePolicy policy = retained.restorePolicy(); if (policy == RestorePolicy.DEFAULT) { // we might have a default in @RetainedConfig if (config.restorePolicy() != policy) { policy = config.restorePolicy(); } } // policy only works on objects as primitives have default // values which we can't really check for :( if (!context.utils().isPrimitive(element.fieldMirror())) { switch (policy) { case IF_NULL: analysis.wrap(s -> "if({{fieldName}} == null){\n" + s + "}\n"); break; case IF_NOT_NULL: analysis.wrap(s -> "if({{fieldName}} != null){\n" + s + "}\n"); break; default: case DEFAULT: case OVERWRITE: // do nothing break; } } } @Override public boolean test(FieldModel fieldModel) { return fieldModel.annotation(Retained.class) .map((retained) -> !retained.skip() && context.config().fieldAllowed(fieldModel)) .orElse(false); } }