All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.auties.protobuf.serialization.ProtobufConverterGraph Maven / Gradle / Ivy

package it.auties.protobuf.serialization;

import it.auties.protobuf.serialization.generator.method.ProtobufMethodGenerator;
import it.auties.protobuf.serialization.support.Types;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import java.util.*;
import java.util.stream.Collectors;

public class ProtobufConverterGraph {
    private final Types types;
    private final Set nodes;
    private int maxGenericLevel;
    public ProtobufConverterGraph(Types types) {
        this.types = types;
        this.nodes = new HashSet<>();
    }

    public void link(TypeMirror from, TypeMirror to, TypeMirror rawGroupParent, ExecutableElement arc) {
        var node = new Node(from, to, rawGroupParent, arc);
        nodes.add(node);
        this.maxGenericLevel = Math.max(maxGenericLevel, count(from));
    }

    private int count(TypeMirror type) {
        if(type.getKind() == TypeKind.TYPEVAR || !(type instanceof DeclaredType declaredType)) {
            return 0;
        }

        var counter = 0;
        for(var param : declaredType.getTypeArguments()) {
            counter += 1 + count(param);
        }

        return counter;
    }

    public List get(TypeMirror from, TypeMirror to, List mixins) {
        var mixinsNames = mixins.stream()
                .map(entry -> entry.getQualifiedName().toString())
                .collect(Collectors.toUnmodifiableSet());
        return get(from, from, to, mixinsNames);
    }

    private List get(TypeMirror originalFrom, TypeMirror currentFrom, TypeMirror currentTo, Set mixins) {
        for(var entry : nodes) {
            if (!types.isAssignable(currentFrom, entry.from())) {
                continue;
            }

            if (isArcIllegal(originalFrom, currentFrom, currentTo, mixins, entry) || !types.isAssignable(currentTo, entry.to())) {
                var results = get(currentFrom, currentTo, mixins, entry, null);
                if (results.isEmpty()) {
                    continue;
                }

                return results;
            }

            if (!types.isParametrized(entry.arc())) {
                return List.of(new Element(entry.arc()));
            }

            var returnType = types.getReturnType(entry.arc(), List.of(currentFrom));
            if (types.isAssignable(currentTo, returnType, false)) {
                return List.of(new Element(entry.arc(), returnType));
            }

            var results = get(currentFrom, currentTo, mixins, entry, returnType);
            if (!results.isEmpty()) {
                return results;
            }
        }

        return List.of();
    }

    private boolean isArcIllegal(TypeMirror originalFrom, TypeMirror from, TypeMirror to, Set mixins, Node entry) {
        var arcOwnerQualifiedName = getDeclaredTypeName(entry.arc().getEnclosingElement().asType());
        if(arcOwnerQualifiedName == null) {
            return true;
        }

        if(mixins.contains(arcOwnerQualifiedName)) {
            return false;
        }

        var fromQualifiedName = getDeclaredTypeName(from);
        var fromQualifiedSpecName = types.isObject(from) ? ProtobufMethodGenerator.getSpecFromObject(from) : null;
        if(Objects.equals(arcOwnerQualifiedName, fromQualifiedName) || Objects.equals(arcOwnerQualifiedName, fromQualifiedSpecName)) {
            return false;
        }

        var toQualifiedName = getDeclaredTypeName(to);
        var toQualifiedSpecName = types.isObject(to) ? ProtobufMethodGenerator.getSpecFromObject(to) : null;
        if (Objects.equals(arcOwnerQualifiedName, toQualifiedName) || Objects.equals(arcOwnerQualifiedName, toQualifiedSpecName)) {
            return false;
        }

        var rawGroupOwner = entry.rawGroupOwner();
        if(rawGroupOwner == null) {
            return true;
        }

        var originalFromQualifiedName = getDeclaredTypeName(originalFrom);
        var rawGroupQualifiedName = getDeclaredTypeName(rawGroupOwner);
        return !Objects.equals(originalFromQualifiedName, rawGroupQualifiedName)
                && !Objects.equals(toQualifiedName, rawGroupQualifiedName);
    }

    private String getDeclaredTypeName(TypeMirror mirror) {
        if (mirror instanceof DeclaredType declaredType && declaredType.asElement() instanceof TypeElement typeElement) {
            return typeElement.getQualifiedName().toString();
        }

        return null;
    }

    private List get(TypeMirror from, TypeMirror to, Set mixins, Node entry, TypeMirror genericReturnType) {
        if (!types.isParametrized(entry.arc())) {
            var nested = get(from, entry.to(), to, mixins);
            if (nested.isEmpty()) {
                return List.of();
            }

            if(entry.rawGroupOwner() != null && isArcIllegal(from, from, to, mixins, entry)) {
                return List.of();
            }

            return getSteps(entry, entry.arc().getReturnType(), nested);
        }

        var returnType = genericReturnType != null ? genericReturnType : types.getReturnType(entry.arc(), List.of(from));
        if (count(returnType) > maxGenericLevel) {
            return List.of();
        }

        var nested = get(from, returnType, to, mixins);
        if (nested.isEmpty()) {
            return List.of();
        }

        if(entry.rawGroupOwner() != null && isArcIllegal(from, from, to, mixins, entry)) {
            return List.of();
        }

        return getSteps(entry, returnType, nested);
    }

    private ArrayList getSteps(Node entry, TypeMirror returnType, List nested) {
        var results = new ArrayList();
        results.add(new Element(entry.arc(), returnType));
        results.addAll(nested);
        return results;
    }

    public record Element(ExecutableElement method, TypeMirror returnType) {
        public Element(ExecutableElement method) {
            this(method, method.getReturnType());
        }
    }

    private record Node(TypeMirror from, TypeMirror to, TypeMirror rawGroupOwner, ExecutableElement arc) {
        @Override
        public int hashCode() {
            return (from + "_" + to).hashCode();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy