in modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistLoader.java [143:496]
public static Whitelist loadFromResourceFiles(Class<?> owner, Map<String, WhitelistAnnotationParser> parsers, String... filepaths) {
List<WhitelistClass> whitelistClasses = new ArrayList<>();
List<WhitelistMethod> whitelistStatics = new ArrayList<>();
List<WhitelistClassBinding> whitelistClassBindings = new ArrayList<>();
// Execute a single pass through the whitelist text files. This will gather all the
// constructors, methods, augmented methods, and fields for each whitelisted class.
for (String filepath : filepaths) {
String line;
int number = -1;
try (
LineNumberReader reader = new LineNumberReader(
new InputStreamReader(getResourceAsStream(owner, filepath), StandardCharsets.UTF_8)
)
) {
String parseType = null;
String whitelistClassOrigin = null;
String javaClassName = null;
List<WhitelistConstructor> whitelistConstructors = null;
List<WhitelistMethod> whitelistMethods = null;
List<WhitelistField> whitelistFields = null;
List<Object> classAnnotations = null;
while ((line = reader.readLine()) != null) {
number = reader.getLineNumber();
line = line.trim();
// Skip any lines that are either blank or comments.
if (line.length() == 0 || line.charAt(0) == '#') {
continue;
}
// Handle a new class by resetting all the variables necessary to construct a new WhitelistClass for the whitelist.
// Expects the following format: 'class' ID annotations? '{' '\n'
if (line.startsWith("class ")) {
// Ensure the final token of the line is '{'.
if (line.endsWith("{") == false) {
throw new IllegalArgumentException(
"invalid class definition: failed to parse class opening bracket [" + line + "]"
);
}
if (parseType != null) {
throw new IllegalArgumentException("invalid definition: cannot embed class definition [" + line + "]");
}
// Parse the Java class name and annotations if they exist.
int annotationIndex = line.indexOf('@');
if (annotationIndex == -1) {
annotationIndex = line.length() - 1;
classAnnotations = Collections.emptyList();
} else {
classAnnotations = parseWhitelistAnnotations(parsers, line.substring(annotationIndex, line.length() - 1));
}
parseType = "class";
whitelistClassOrigin = "[" + filepath + "]:[" + number + "]";
javaClassName = line.substring(5, annotationIndex).trim();
// Reset all the constructors, methods, and fields to support a new class.
whitelistConstructors = new ArrayList<>();
whitelistMethods = new ArrayList<>();
whitelistFields = new ArrayList<>();
} else if (line.startsWith("static_import ")) {
// Ensure the final token of the line is '{'.
if (line.endsWith("{") == false) {
throw new IllegalArgumentException(
"invalid static import definition: failed to parse static import opening bracket [" + line + "]"
);
}
if (parseType != null) {
throw new IllegalArgumentException("invalid definition: cannot embed static import definition [" + line + "]");
}
parseType = "static_import";
// Handle the end of a definition and reset all previously gathered values.
// Expects the following format: '}' '\n'
} else if (line.equals("}")) {
if (parseType == null) {
throw new IllegalArgumentException("invalid definition: extraneous closing bracket");
}
// Create a new WhitelistClass with all the previously gathered constructors, methods,
// augmented methods, and fields, and add it to the list of whitelisted classes.
if ("class".equals(parseType)) {
whitelistClasses.add(
new WhitelistClass(
whitelistClassOrigin,
javaClassName,
whitelistConstructors,
whitelistMethods,
whitelistFields,
classAnnotations
)
);
whitelistClassOrigin = null;
javaClassName = null;
whitelistConstructors = null;
whitelistMethods = null;
whitelistFields = null;
classAnnotations = null;
}
// Reset the parseType.
parseType = null;
// Handle static import definition types.
// Expects the following format: ID ID '(' ( ID ( ',' ID )* )? ')' ( 'from_class' | 'bound_to' ) ID annotations?
// '\n'
} else if ("static_import".equals(parseType)) {
// Mark the origin of this parsable object.
String origin = "[" + filepath + "]:[" + number + "]";
// Parse the tokens prior to the method parameters.
int parameterStartIndex = line.indexOf('(');
if (parameterStartIndex == -1) {
throw new IllegalArgumentException(
"illegal static import definition: start of method parameters not found [" + line + "]"
);
}
String[] tokens = line.substring(0, parameterStartIndex).trim().split("\\s+");
String methodName;
// Based on the number of tokens, look up the Java method name.
if (tokens.length == 2) {
methodName = tokens[1];
} else {
throw new IllegalArgumentException("invalid method definition: unexpected format [" + line + "]");
}
String returnCanonicalTypeName = tokens[0];
// Parse the method parameters.
int parameterEndIndex = line.indexOf(')');
if (parameterEndIndex == -1) {
throw new IllegalArgumentException(
"illegal static import definition: end of method parameters not found [" + line + "]"
);
}
String[] canonicalTypeNameParameters = line.substring(parameterStartIndex + 1, parameterEndIndex)
.replaceAll("\\s+", "")
.split(",");
// Handle the case for a method with no parameters.
if ("".equals(canonicalTypeNameParameters[0])) {
canonicalTypeNameParameters = new String[0];
}
// Parse the annotations if they exist.
List<Object> annotations;
int annotationIndex = line.indexOf('@');
if (annotationIndex == -1) {
annotationIndex = line.length();
annotations = Collections.emptyList();
} else {
annotations = parseWhitelistAnnotations(parsers, line.substring(annotationIndex));
}
// Parse the static import type and class.
tokens = line.substring(parameterEndIndex + 1, annotationIndex).trim().split("\\s+");
String staticImportType;
String targetJavaClassName;
// Based on the number of tokens, look up the type and class.
if (tokens.length == 2) {
staticImportType = tokens[0];
targetJavaClassName = tokens[1];
} else {
throw new IllegalArgumentException("invalid static import definition: unexpected format [" + line + "]");
}
// Add a static import method or binding depending on the static import type.
if ("from_class".equals(staticImportType)) {
whitelistStatics.add(
new WhitelistMethod(
origin,
targetJavaClassName,
methodName,
returnCanonicalTypeName,
Arrays.asList(canonicalTypeNameParameters),
annotations
)
);
} else if ("bound_to".equals(staticImportType)) {
whitelistClassBindings.add(
new WhitelistClassBinding(
origin,
targetJavaClassName,
methodName,
returnCanonicalTypeName,
List.of(canonicalTypeNameParameters),
annotations
)
);
} else {
throw new IllegalArgumentException(
"invalid static import definition: "
+ "unexpected static import type ["
+ staticImportType
+ "] ["
+ line
+ "]"
);
}
// Handle class definition types.
} else if ("class".equals(parseType)) {
// Mark the origin of this parsable object.
String origin = "[" + filepath + "]:[" + number + "]";
// Handle the case for a constructor definition.
// Expects the following format: '(' ( ID ( ',' ID )* )? ')' annotations? '\n'
if (line.startsWith("(")) {
// Parse the constructor parameters.
int parameterEndIndex = line.indexOf(')');
if (parameterEndIndex == -1) {
throw new IllegalArgumentException(
"illegal constructor definition: end of constructor parameters not found [" + line + "]"
);
}
String[] canonicalTypeNameParameters = line.substring(1, parameterEndIndex).replaceAll("\\s+", "").split(",");
// Handle the case for a constructor with no parameters.
if ("".equals(canonicalTypeNameParameters[0])) {
canonicalTypeNameParameters = new String[0];
}
// Parse the annotations if they exist.
List<Object> annotations;
int annotationIndex = line.indexOf('@');
annotations = annotationIndex == -1
? Collections.emptyList()
: parseWhitelistAnnotations(parsers, line.substring(annotationIndex));
whitelistConstructors.add(new WhitelistConstructor(origin, List.of(canonicalTypeNameParameters), annotations));
// Handle the case for a method or augmented method definition.
// Expects the following format: ID ID? ID '(' ( ID ( ',' ID )* )? ')' annotations? '\n'
} else if (line.contains("(")) {
// Parse the tokens prior to the method parameters.
int parameterStartIndex = line.indexOf('(');
String[] tokens = line.substring(0, parameterStartIndex).trim().split("\\s+");
String methodName;
String javaAugmentedClassName;
// Based on the number of tokens, look up the Java method name and if provided the Java augmented class.
if (tokens.length == 2) {
methodName = tokens[1];
javaAugmentedClassName = null;
} else if (tokens.length == 3) {
methodName = tokens[2];
javaAugmentedClassName = tokens[1];
} else {
throw new IllegalArgumentException("invalid method definition: unexpected format [" + line + "]");
}
String returnCanonicalTypeName = tokens[0];
// Parse the method parameters.
int parameterEndIndex = line.indexOf(')');
if (parameterEndIndex == -1) {
throw new IllegalArgumentException(
"illegal static import definition: end of method parameters not found [" + line + "]"
);
}
String[] canonicalTypeNameParameters = line.substring(parameterStartIndex + 1, parameterEndIndex)
.replaceAll("\\s+", "")
.split(",");
// Handle the case for a method with no parameters.
if ("".equals(canonicalTypeNameParameters[0])) {
canonicalTypeNameParameters = new String[0];
}
// Parse the annotations if they exist.
List<Object> annotations;
int annotationIndex = line.indexOf('@');
annotations = annotationIndex == -1
? Collections.emptyList()
: parseWhitelistAnnotations(parsers, line.substring(annotationIndex));
whitelistMethods.add(
new WhitelistMethod(
origin,
javaAugmentedClassName,
methodName,
returnCanonicalTypeName,
Arrays.asList(canonicalTypeNameParameters),
annotations
)
);
// Handle the case for a field definition.
// Expects the following format: ID ID annotations? '\n'
} else {
// Parse the annotations if they exist.
List<Object> annotations;
int annotationIndex = line.indexOf('@');
if (annotationIndex == -1) {
annotationIndex = line.length();
annotations = Collections.emptyList();
} else {
annotations = parseWhitelistAnnotations(parsers, line.substring(annotationIndex));
}
// Parse the field tokens.
String[] tokens = line.substring(0, annotationIndex).split("\\s+");
// Ensure the correct number of tokens.
if (tokens.length != 2) {
throw new IllegalArgumentException("invalid field definition: unexpected format [" + line + "]");
}
whitelistFields.add(new WhitelistField(origin, tokens[1], tokens[0], annotations));
}
} else {
throw new IllegalArgumentException("invalid definition: unable to parse line [" + line + "]");
}
}
// Ensure all classes end with a '}' token before the end of the file.
if (javaClassName != null) {
throw new IllegalArgumentException("invalid definition: expected closing bracket");
}
} catch (ResourceNotFoundException e) {
throw e; // rethrow
} catch (Exception exception) {
throw new RuntimeException("error in [" + filepath + "] at line [" + number + "]", exception);
}
}
ClassLoader loader = owner.getClassLoader();
return new Whitelist(loader, whitelistClasses, whitelistStatics, whitelistClassBindings, Collections.emptyList());
}