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

io.quarkiverse.langchain4j.deployment.JandexUtil Maven / Gradle / Ivy

The newest version!
package io.quarkiverse.langchain4j.deployment;

import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import org.jboss.jandex.ArrayType;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.ClassType;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.ParameterizedType;
import org.jboss.jandex.PrimitiveType;
import org.jboss.jandex.Type;
import org.jboss.jandex.TypeVariable;
import org.jboss.logging.Logger;

class JandexUtil {

    private static final Logger log = Logger.getLogger(JandexUtil.class);

    static Collection getAllSuperinterfaces(ClassInfo interfaceName, IndexView index) {
        Set result = new HashSet<>();

        Queue workQueue = new ArrayDeque<>();
        Set alreadyProcessed = new HashSet<>();

        workQueue.add(interfaceName.name());
        while (!workQueue.isEmpty()) {
            DotName iface = workQueue.remove();
            if (!alreadyProcessed.add(iface)) {
                continue;
            }

            List directSuperInterfaces = new ArrayList<>();
            for (DotName name : interfaceName.interfaceNames()) {
                ClassInfo classInfo = index.getClassByName(name);
                if (classInfo == null) {
                    log.warn("'" + name
                            + "' used for creating an AiService is not an interface. Attempting to create an AiService using this class will fail");
                }
                directSuperInterfaces.add(classInfo);
            }
            for (ClassInfo directSubInterface : directSuperInterfaces) {
                result.add(directSubInterface);
                workQueue.add(directSubInterface.name());
            }
        }

        return result;
    }

    static boolean isDefault(short flags) {
        return ((flags & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC);
    }

    static Class load(Type type, ClassLoader classLoader) {
        if (type.kind() == Type.Kind.PRIMITIVE) {
            PrimitiveType prim = type.asPrimitiveType();
            switch (prim.primitive()) {
                case INT:
                    return int.class;
                case BYTE:
                    return byte.class;
                case CHAR:
                    return char.class;
                case LONG:
                    return long.class;
                case FLOAT:
                    return float.class;
                case SHORT:
                    return short.class;
                case DOUBLE:
                    return double.class;
                case BOOLEAN:
                    return boolean.class;
                default:
                    throw new RuntimeException("Unknown type " + prim.primitive());
            }
        } else {
            try {
                return Class.forName(type.name().toString(), false, classLoader);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * Returns the captured generic types of an interface given a class that at some point in the class
     * hierarchy implements the interface.
     *
     * The list contains the types in the same order as they are generic parameters defined on the interface
     *
     * A result is only returned if and only if all the generics where captured. If any of them where not defined by the class
     * an exception is thrown.
     *
     * Also note that all parts of the class/interface hierarchy must be in the supplied index
     *
     * As an example, imagine the following class:
     *
     * 
     *
     * class MyList implements List<String> {
     *     ...
     * }
     *
     * 
* * If we call * *
     *
     * JandexUtil.resolveTypeParameters(DotName.createSimple(MyList.class.getName()),
     *         DotName.createSimple(List.class.getName()), index)
     *
     * 
* * then the result will contain a single element of class ClassType whose name() would return a DotName for String */ static List resolveTypeParameters(DotName input, DotName target, IndexView index) { final ClassInfo inputClassInfo = fetchFromIndex(input, index); Type startingType = getType(inputClassInfo, index); final List result = findParametersRecursively(startingType, target, new HashSet<>(), index); // null means not found if (result == null) { return Collections.emptyList(); } return result; } /** * Creates a type for a ClassInfo */ private static Type getType(ClassInfo inputClassInfo, IndexView index) { List typeParameters = inputClassInfo.typeParameters(); if (typeParameters.isEmpty()) return ClassType.create(inputClassInfo.name(), Type.Kind.CLASS); Type owner = null; // ignore owners for non-static classes if (inputClassInfo.enclosingClass() != null && !Modifier.isStatic(inputClassInfo.flags())) { owner = getType(fetchFromIndex(inputClassInfo.enclosingClass(), index), index); } return ParameterizedType.create(inputClassInfo.name(), typeParameters.toArray(new Type[0]), owner); } /** * Finds the type arguments passed from the starting type to the given target type, mapping * generics when found, on the way down. Returns null if not found. */ private static List findParametersRecursively(Type type, DotName target, Set visitedTypes, IndexView index) { DotName name = type.name(); // cache results first if (!visitedTypes.add(name)) { return null; } // always end at Object if (DotNames.OBJECT.equals(name) || DotNames.RECORD.equals(name)) { return null; } final ClassInfo inputClassInfo = fetchFromIndex(name, index); // look at the current type if (target.equals(name)) { Type thisType = getType(inputClassInfo, index); if (thisType.kind() == Type.Kind.CLASS) return Collections.emptyList(); else return thisType.asParameterizedType().arguments(); } // superclasses first Type superClassType = inputClassInfo.superClassType(); List superResult = findParametersRecursively(superClassType, target, visitedTypes, index); if (superResult != null) { // map any returned type parameters to our type arguments on the way down return mapTypeArguments(superClassType, superResult, index); } // interfaces second for (Type interfaceType : inputClassInfo.interfaceTypes()) { List ret = findParametersRecursively(interfaceType, target, visitedTypes, index); if (ret != null) { // map any returned type parameters to our type arguments on the way down return mapTypeArguments(interfaceType, ret, index); } } // not found return null; } /** * Maps any type parameters in typeArgumentsFromSupertype from the type parameters declared in appliedType's declaration * to the type arguments we passed in appliedType */ private static List mapTypeArguments(Type appliedType, List typeArgumentsFromSupertype, IndexView index) { // no type arguments to map if (typeArgumentsFromSupertype.isEmpty()) { return typeArgumentsFromSupertype; } // extra easy if all the type args don't contain any type parameters if (!containsTypeParameters(typeArgumentsFromSupertype)) { return typeArgumentsFromSupertype; } // this can't fail since we got a result ClassInfo superType = fetchFromIndex(appliedType.name(), index); // if our supertype has no type parameters, we don't need any mapping if (superType.typeParameters().isEmpty()) { return typeArgumentsFromSupertype; } // figure out which arguments we passed to the supertype List appliedArguments; // we passed them explicitely if (appliedType.kind() == Type.Kind.PARAMETERIZED_TYPE) { appliedArguments = appliedType.asParameterizedType().arguments(); } else { // raw supertype: use bounds appliedArguments = new ArrayList<>(superType.typeParameters().size()); for (TypeVariable typeVariable : superType.typeParameters()) { if (!typeVariable.bounds().isEmpty()) { appliedArguments.add(typeVariable.bounds().get(0)); } else { appliedArguments.add(ClassType.create(DotNames.OBJECT, Type.Kind.CLASS)); } } } // it's a problem if we got different arguments to the parameters declared if (appliedArguments.size() != superType.typeParameters().size()) { throw new IllegalArgumentException("Our supertype instance " + appliedType + " does not match supertype declared arguments: " + superType.typeParameters()); } // build the mapping Map mapping = new HashMap<>(); for (int i = 0; i < superType.typeParameters().size(); i++) { TypeVariable typeParameter = superType.typeParameters().get(i); mapping.put(typeParameter.identifier(), appliedArguments.get(i)); } // and map return mapGenerics(typeArgumentsFromSupertype, mapping); } private static boolean containsTypeParameters(List typeArgumentsFromSupertype) { for (Type type : typeArgumentsFromSupertype) { if (containsTypeParameters(type)) { return true; } } return false; } private static boolean containsTypeParameters(Type type) { switch (type.kind()) { case ARRAY: return containsTypeParameters(type.asArrayType().constituent()); case PARAMETERIZED_TYPE: ParameterizedType parameterizedType = type.asParameterizedType(); if (parameterizedType.owner() != null && containsTypeParameters(parameterizedType.owner())) return true; return containsTypeParameters(parameterizedType.arguments()); case TYPE_VARIABLE: return true; default: return false; } } private static List mapGenerics(List types, Map mapping) { List ret = new ArrayList<>(types.size()); for (Type type : types) { ret.add(mapGenerics(type, mapping)); } return ret; } private static Type mapGenerics(Type type, Map mapping) { switch (type.kind()) { case ARRAY: ArrayType arrayType = type.asArrayType(); return ArrayType.create(mapGenerics(arrayType.constituent(), mapping), arrayType.dimensions()); case CLASS: return type; case PARAMETERIZED_TYPE: ParameterizedType parameterizedType = type.asParameterizedType(); Type owner = null; if (parameterizedType.owner() != null) { owner = mapGenerics(parameterizedType.owner(), mapping); } return ParameterizedType.create(parameterizedType.name(), mapGenerics(parameterizedType.arguments(), mapping).toArray(new Type[0]), owner); case TYPE_VARIABLE: Type ret = mapping.get(type.asTypeVariable().identifier()); if (ret == null) { throw new IllegalArgumentException("Missing type argument mapping for " + type); } return ret; default: throw new IllegalArgumentException("Illegal type in hierarchy: " + type); } } private static ClassInfo fetchFromIndex(DotName dotName, IndexView index) { final ClassInfo classInfo = index.getClassByName(dotName); if (classInfo == null) { throw new IllegalArgumentException("Class " + dotName + " was not found in the index"); } return classInfo; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy