public void addPainlessClassBinding()

in modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookupBuilder.java [1423:1690]


    public void addPainlessClassBinding(
        Class<?> targetClass,
        String methodName,
        Class<?> returnType,
        List<Class<?>> typeParameters,
        Map<Class<?>, Object> annotations
    ) {
        Objects.requireNonNull(targetClass);
        Objects.requireNonNull(methodName);
        Objects.requireNonNull(returnType);
        Objects.requireNonNull(typeParameters);

        if (targetClass == def.class) {
            throw new IllegalArgumentException("cannot add class binding as reserved class [" + DEF_CLASS_NAME + "]");
        }

        String targetCanonicalClassName = typeToCanonicalTypeName(targetClass);
        Class<?> existingTargetClass = javaClassNamesToClasses.get(targetClass.getName());

        if (existingTargetClass == null) {
            javaClassNamesToClasses.put(targetClass.getName().intern(), targetClass);
        } else if (existingTargetClass != targetClass) {
            throw new IllegalArgumentException(
                "class ["
                    + targetCanonicalClassName
                    + "] "
                    + "cannot represent multiple java classes with the same name from different class loaders"
            );
        }

        Constructor<?>[] javaConstructors = targetClass.getConstructors();
        Constructor<?> javaConstructor = null;

        for (Constructor<?> eachJavaConstructor : javaConstructors) {
            if (eachJavaConstructor.getDeclaringClass() == targetClass) {
                if (javaConstructor != null) {
                    throw new IllegalArgumentException(
                        "class binding [" + targetCanonicalClassName + "] cannot have multiple constructors"
                    );
                }

                javaConstructor = eachJavaConstructor;
            }
        }

        if (javaConstructor == null) {
            throw new IllegalArgumentException("class binding [" + targetCanonicalClassName + "] must have exactly one constructor");
        }

        int constructorTypeParametersSize = javaConstructor.getParameterCount();

        for (int typeParameterIndex = 0; typeParameterIndex < constructorTypeParametersSize; ++typeParameterIndex) {
            Class<?> typeParameter = typeParameters.get(typeParameterIndex);

            if (isValidType(typeParameter) == false) {
                throw new IllegalArgumentException(
                    "type parameter ["
                        + typeToCanonicalTypeName(typeParameter)
                        + "] not found "
                        + "for class binding [["
                        + targetCanonicalClassName
                        + "], "
                        + typesToCanonicalTypeNames(typeParameters)
                        + "]"
                );
            }

            Class<?> javaTypeParameter = javaConstructor.getParameterTypes()[typeParameterIndex];

            if (isValidType(javaTypeParameter) == false) {
                throw new IllegalArgumentException(
                    "type parameter ["
                        + typeToCanonicalTypeName(typeParameter)
                        + "] not found "
                        + "for class binding [["
                        + targetCanonicalClassName
                        + "], "
                        + typesToCanonicalTypeNames(typeParameters)
                        + "]"
                );
            }

            if (javaTypeParameter != typeToJavaType(typeParameter)) {
                throw new IllegalArgumentException(
                    "type parameter ["
                        + typeToCanonicalTypeName(javaTypeParameter)
                        + "] "
                        + "does not match the specified type parameter ["
                        + typeToCanonicalTypeName(typeParameter)
                        + "] "
                        + "for class binding [["
                        + targetClass.getCanonicalName()
                        + "], "
                        + typesToCanonicalTypeNames(typeParameters)
                        + "]"
                );
            }
        }

        if (METHOD_NAME_PATTERN.matcher(methodName).matches() == false) {
            throw new IllegalArgumentException(
                "invalid method name [" + methodName + "] for class binding [" + targetCanonicalClassName + "]."
            );
        }

        Method[] javaMethods = targetClass.getMethods();
        Method javaMethod = null;

        for (Method eachJavaMethod : javaMethods) {
            if (eachJavaMethod.getDeclaringClass() == targetClass) {
                if (javaMethod != null) {
                    throw new IllegalArgumentException("class binding [" + targetCanonicalClassName + "] cannot have multiple methods");
                }

                javaMethod = eachJavaMethod;
            }
        }

        if (javaMethod == null) {
            throw new IllegalArgumentException("class binding [" + targetCanonicalClassName + "] must have exactly one method");
        }

        int methodTypeParametersSize = javaMethod.getParameterCount();

        for (int typeParameterIndex = 0; typeParameterIndex < methodTypeParametersSize; ++typeParameterIndex) {
            Class<?> typeParameter = typeParameters.get(constructorTypeParametersSize + typeParameterIndex);

            if (isValidType(typeParameter) == false) {
                throw new IllegalArgumentException(
                    "type parameter ["
                        + typeToCanonicalTypeName(typeParameter)
                        + "] not found "
                        + "for class binding [["
                        + targetCanonicalClassName
                        + "], "
                        + typesToCanonicalTypeNames(typeParameters)
                        + "]"
                );
            }

            Class<?> javaTypeParameter = javaMethod.getParameterTypes()[typeParameterIndex];

            if (isValidType(javaTypeParameter) == false) {
                throw new IllegalArgumentException(
                    "type parameter ["
                        + typeToCanonicalTypeName(typeParameter)
                        + "] not found "
                        + "for class binding [["
                        + targetCanonicalClassName
                        + "], "
                        + typesToCanonicalTypeNames(typeParameters)
                        + "]"
                );
            }

            if (javaTypeParameter != typeToJavaType(typeParameter)) {
                throw new IllegalArgumentException(
                    "type parameter ["
                        + typeToCanonicalTypeName(javaTypeParameter)
                        + "] "
                        + "does not match the specified type parameter ["
                        + typeToCanonicalTypeName(typeParameter)
                        + "] "
                        + "for class binding [["
                        + targetClass.getCanonicalName()
                        + "], "
                        + typesToCanonicalTypeNames(typeParameters)
                        + "]"
                );
            }
        }

        if (isValidType(returnType) == false) {
            throw new IllegalArgumentException(
                "return type ["
                    + typeToCanonicalTypeName(returnType)
                    + "] not found for class binding "
                    + "[["
                    + targetCanonicalClassName
                    + "], ["
                    + methodName
                    + "], "
                    + typesToCanonicalTypeNames(typeParameters)
                    + "]"
            );
        }

        if (javaMethod.getReturnType() != typeToJavaType(returnType)) {
            throw new IllegalArgumentException(
                "return type ["
                    + typeToCanonicalTypeName(javaMethod.getReturnType())
                    + "] "
                    + "does not match the specified returned type ["
                    + typeToCanonicalTypeName(returnType)
                    + "] "
                    + "for class binding [["
                    + targetClass.getCanonicalName()
                    + "], ["
                    + methodName
                    + "], "
                    + typesToCanonicalTypeNames(typeParameters)
                    + "]"
            );
        }

        String painlessMethodKey = buildPainlessMethodKey(methodName, constructorTypeParametersSize + methodTypeParametersSize);

        if (painlessMethodKeysToImportedPainlessMethods.containsKey(painlessMethodKey)) {
            throw new IllegalArgumentException("class binding and imported method cannot have the same name [" + methodName + "]");
        }

        if (painlessMethodKeysToPainlessInstanceBindings.containsKey(painlessMethodKey)) {
            throw new IllegalArgumentException("class binding and instance binding cannot have the same name [" + methodName + "]");
        }

        if (Modifier.isStatic(javaMethod.getModifiers())) {
            throw new IllegalArgumentException(
                "class binding [["
                    + targetClass.getCanonicalName()
                    + "], ["
                    + methodName
                    + "], "
                    + typesToCanonicalTypeNames(typeParameters)
                    + "] cannot be static"
            );
        }

        PainlessClassBinding existingPainlessClassBinding = painlessMethodKeysToPainlessClassBindings.get(painlessMethodKey);
        PainlessClassBinding newPainlessClassBinding = new PainlessClassBinding(
            javaConstructor,
            javaMethod,
            returnType,
            typeParameters,
            annotations
        );

        if (existingPainlessClassBinding == null) {
            newPainlessClassBinding = painlessClassBindingCache.computeIfAbsent(newPainlessClassBinding, key -> key);
            painlessMethodKeysToPainlessClassBindings.put(painlessMethodKey.intern(), newPainlessClassBinding);
        } else if (newPainlessClassBinding.equals(existingPainlessClassBinding) == false) {
            throw new IllegalArgumentException(
                "cannot add class bindings with the same name and arity "
                    + "but do not have equivalent methods "
                    + "[["
                    + targetCanonicalClassName
                    + "], "
                    + "["
                    + methodName
                    + "], "
                    + "["
                    + typeToCanonicalTypeName(returnType)
                    + "], "
                    + typesToCanonicalTypeNames(typeParameters)
                    + "] and "
                    + "[["
                    + targetCanonicalClassName
                    + "], "
                    + "["
                    + methodName
                    + "], "
                    + "["
                    + typeToCanonicalTypeName(existingPainlessClassBinding.returnType)
                    + "], "
                    + typesToCanonicalTypeNames(existingPainlessClassBinding.typeParameters)
                    + "]"
            );
        }
    }