it.auties.protobuf.serialization.generator.method.ProtobufMethodGenerator Maven / Gradle / Ivy
package it.auties.protobuf.serialization.generator.method;
import it.auties.protobuf.annotation.ProtobufEnum;
import it.auties.protobuf.annotation.ProtobufGroup;
import it.auties.protobuf.annotation.ProtobufMessage;
import it.auties.protobuf.serialization.support.JavaWriter.ClassWriter;
import it.auties.protobuf.serialization.support.JavaWriter.ClassWriter.MethodWriter;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public abstract class ProtobufMethodGenerator {
protected final INPUT objectElement;
protected final List deferredOperations;
protected ProtobufMethodGenerator(INPUT objectElement) {
this.objectElement = objectElement;
this.deferredOperations = new ArrayList<>();
}
public void generate(ClassWriter writer) {
if (!shouldInstrument()) {
return;
}
var parametersTypes = parametersTypes();
var parametersNames = parametersNames();
if(parametersTypes.size() != parametersNames.size()) {
throw new IllegalArgumentException("Parameters mismatch");
}
var parameters = IntStream.range(0, parametersTypes.size())
.mapToObj(index -> parametersTypes.get(index) + " " + parametersNames.get(index))
.toArray(String[]::new);
try(var methodWriter = writer.printMethodDeclaration(modifiers(), returnType(), name(), parameters)) {
doInstrumentation(writer, methodWriter);
}
while (!deferredOperations.isEmpty()) {
var round = new ArrayList<>(deferredOperations);
deferredOperations.clear();
for(var runnable : round) {
runnable.run();
}
}
}
public abstract boolean shouldInstrument();
protected abstract void doInstrumentation(ClassWriter classWriter, MethodWriter writer);
protected abstract List modifiers();
protected abstract String returnType();
protected abstract String name();
protected abstract List parametersTypes();
protected abstract List parametersNames();
public static String getSpecFromObject(TypeMirror typeMirror) {
if(!(typeMirror instanceof DeclaredType declaredType)) {
return "";
}
var element = (TypeElement) declaredType.asElement();
var parent = element.getEnclosingElement();
String packageName = null;
var name = new StringBuilder();
while (parent != null) {
if(parent instanceof TypeElement typeElement) {
name.append(typeElement.getSimpleName());
}else if(parent instanceof PackageElement packageElement) {
packageName = packageElement.getQualifiedName().toString();
break;
}
parent = parent.getEnclosingElement();
}
name.append(declaredType.asElement().getSimpleName());
var result = new StringBuilder();
if(packageName != null) {
result.append(packageName);
result.append(".");
}
result.append(name);
result.append("Spec");
return result.toString();
}
protected String getAccessorCall(String object, Element accessor) {
return switch (accessor) {
case ExecutableElement executableElement -> "%s.%s()".formatted(object, executableElement.getSimpleName());
case VariableElement variableElement -> "%s.%s".formatted(object, variableElement.getSimpleName());
default -> throw new IllegalStateException("Unexpected value: " + accessor);
};
}
protected String getQualifiedName(TypeMirror type) {
if(!(type instanceof DeclaredType declaredType)) {
return type.toString();
}
if((!(declaredType.asElement() instanceof TypeElement typeElement))) {
return declaredType.toString();
}
return typeElement.getQualifiedName().toString();
}
protected String getSimpleName(TypeMirror type) {
var parts = getQualifiedName(type).split("\\.");
return parts[parts.length - 1].replaceAll("\\$", ".");
}
protected boolean isMessage(TypeMirror deserializedType) {
return deserializedType instanceof DeclaredType declaredType
&& declaredType.asElement().getAnnotation(ProtobufMessage.class) != null;
}
protected boolean isGroup(TypeMirror deserializedType) {
return deserializedType instanceof DeclaredType declaredType
&& declaredType.asElement().getAnnotation(ProtobufGroup.class) != null;
}
protected boolean isEnum(TypeMirror deserializedType) {
return deserializedType instanceof DeclaredType declaredType
&& declaredType.asElement().getAnnotation(ProtobufEnum.class) != null;
}
}