package com.sora.util.akatsuki.analyzers;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import javax.lang.model.type.TypeMirror;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Iterables;
import com.sora.util.akatsuki.analyzers.CascadingTypeAnalyzer.CodeTransform;
import com.sora.util.akatsuki.models.FieldModel;
import com.sora.util.akatsuki.models.FieldModel.Flag;
public class Element<T extends TypeMirror> {
private final FieldModel model;
private final List<CodeTransform> keyNameTransformations;
private final List<CodeTransform> fieldAccessorTransformations;
private String accessorExpression;
private TypeMirror fieldMirror;
private T refinedMirror;
@SuppressWarnings("unchecked")
public Element(FieldModel model) {
this.model = model;
this.keyNameTransformations = new ArrayList<>();
this.fieldAccessorTransformations = new ArrayList<>();
this.accessorExpression = "";
this.fieldMirror = model.type();
this.refinedMirror = (T) model.type();
}
// copy constructor
@SuppressWarnings("unchecked")
private Element(Element<?> element) {
this.model = element.model;
this.keyNameTransformations = new ArrayList<>(element.keyNameTransformations);
this.fieldAccessorTransformations = new ArrayList<>(element.fieldAccessorTransformations);
this.accessorExpression = element.accessorExpression;
this.fieldMirror = element.fieldMirror;
this.refinedMirror = (T) element.refinedMirror;
}
public javax.lang.model.element.Element originatingElement() {
return model.element;
}
public FieldModel model() {
return model;
}
public String accessor(Function<String, String> fieldAccessFunction) {
String fieldAccess = fieldAccessFunction.apply(model.name() + accessorExpression);
for (Function<String, String> transformations : fieldAccessorTransformations) {
fieldAccess = transformations.apply(fieldAccess);
}
return fieldAccess;
}
public String uniqueName() {
if (model.flags().contains(Flag.HIDDEN)) {
// fieldName_packageName
return model.name() + "_" + model.enclosingElement().getQualifiedName();
} else {
// fieldName
return model.name();
}
}
public String keyName() {
String keyName = "\"" + uniqueName() + "\"";
for (Function<String, String> transformations : keyNameTransformations) {
keyName = transformations.apply(keyName);
}
return keyName;
}
public TypeMirror fieldMirror() {
return fieldMirror;
}
public T refinedMirror() {
return refinedMirror;
}
public Builder<T> toBuilder() {
return new Builder<>(this);
}
public <NT extends TypeMirror> Element<NT> refine(NT newTypeMirror) {
return toBuilder().refinedType(newTypeMirror).build();
}
public static class Builder<T extends TypeMirror> {
public enum SetterMode {
APPEND((o, i) -> o + i), PREPEND((o, i) -> i + o), REPLACE((o, i) -> i);
private final BiFunction<String, String, String> function;
SetterMode(BiFunction<String, String, String> function) {
this.function = function;
}
public String process(String original, String input) {
return function.apply(original, input);
}
}
private Element<T> element;
private Builder(Element<T> element) {
this.element = new Element<>(element);
}
public <NT extends TypeMirror> Builder<NT> type(NT type) {
return fieldType(type).refinedType(type);
}
public Builder<T> fieldType(TypeMirror mirror) {
element.fieldMirror = mirror;
return this;
}
@SuppressWarnings("unchecked")
public <NT extends TypeMirror> Builder<NT> refinedType(NT mirror) {
element.refinedMirror = (T) mirror;
return (Builder<NT>) this;
}
public Builder<T> keyName(SetterMode mode, String name) {
element.keyNameTransformations.add((original) -> mode.process(original, name));
return this;
}
public Builder<T> fieldNameTransforms(CodeTransform transform) {
element.fieldAccessorTransformations.add(transform);
return this;
}
public Builder<T> accessor(SetterMode mode, String accessor) {
element.accessorExpression = mode.process(element.accessorExpression, accessor);
return this;
}
public Element<T> build() {
final Element<T> element = this.element;
// forbid mutation through the builder once the object has been
// build
this.element = null;
return element;
}
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("keyNameTransformations", Iterables.size(keyNameTransformations))
.add("accessorExpression", accessorExpression).add("fieldMirror", fieldMirror)
.add("refinedMirror", refinedMirror).toString();
}
}