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);
}
}