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)
+ "]"
);
}
}