/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
package org.opensearch.painless;
import org.opensearch.common.SuppressForbidden;
import org.opensearch.common.io.PathUtils;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.common.xcontent.json.JsonXContent;
import org.opensearch.common.util.io.IOUtils;
import org.opensearch.painless.action.PainlessContextClassBindingInfo;
import org.opensearch.painless.action.PainlessContextClassInfo;
import org.opensearch.painless.action.PainlessContextConstructorInfo;
import org.opensearch.painless.action.PainlessContextFieldInfo;
import org.opensearch.painless.action.PainlessContextInfo;
import org.opensearch.painless.action.PainlessContextInstanceBindingInfo;
import org.opensearch.painless.action.PainlessContextMethodInfo;
import java.io.IOException;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* The gradle task generateContextDoc uses ContextDocGenerator to rebuild
* the Painless API documentation from a clean state after the
* existing documentation is deleted. The following pages are generated:
*
* An index page with each context and links to the APIs
* A high-level overview page of shared API for all contexts
* A detailed page per package per context of shared API for all contexts
* A high-level overview page of specialized API for each context
* A detailed page per package per context of specialized API for each context
*
* Use the docs build to generate HTML pages from the resultant asciidoc files.
*/
public final class ContextDocGenerator {
private static final String SHARED_HEADER = "painless-api-reference-shared";
private static final String SHARED_NAME = "Shared";
public static void main(String[] args) throws IOException {
List contextInfos = getContextInfos();
Set sharedStaticInfos = createSharedStatics(contextInfos);
Set sharedClassInfos = createSharedClasses(contextInfos);
Path rootDir = resetRootDir();
Path sharedDir = createSharedDir(rootDir);
List staticInfos = sortStaticInfos(Collections.emptySet(), new ArrayList<>(sharedStaticInfos));
List classInfos = sortClassInfos(Collections.emptySet(), new ArrayList<>(sharedClassInfos));
Map javaNamesToDisplayNames = getDisplayNames(classInfos);
printSharedIndexPage(sharedDir, javaNamesToDisplayNames, staticInfos, classInfos);
printSharedPackagesPages(sharedDir, javaNamesToDisplayNames, classInfos);
Set isSpecialized = new HashSet<>();
for (PainlessContextInfo contextInfo : contextInfos) {
staticInfos = createContextStatics(contextInfo);
staticInfos = sortStaticInfos(sharedStaticInfos, staticInfos);
classInfos = sortClassInfos(sharedClassInfos, new ArrayList<>(contextInfo.getClasses()));
if (staticInfos.isEmpty() == false || classInfos.isEmpty() == false) {
Path contextDir = createContextDir(rootDir, contextInfo);
isSpecialized.add(contextInfo);
javaNamesToDisplayNames = getDisplayNames(contextInfo.getClasses());
printContextIndexPage(contextDir, javaNamesToDisplayNames, contextInfo, staticInfos, classInfos);
printContextPackagesPages(contextDir, javaNamesToDisplayNames, sharedClassInfos, contextInfo, classInfos);
}
}
printRootIndexPage(rootDir, contextInfos, isSpecialized);
}
@SuppressForbidden(reason = "retrieving data from an internal API not exposed as part of the REST client")
private static List getContextInfos() throws IOException {
URLConnection getContextNames = new URL("http://" + System.getProperty("cluster.uri") + "/_scripts/painless/_context")
.openConnection();
XContentParser parser = JsonXContent.jsonXContent.createParser(null, null, getContextNames.getInputStream());
parser.nextToken();
parser.nextToken();
@SuppressWarnings("unchecked")
List contextNames = (List) (Object) parser.list();
parser.close();
((HttpURLConnection) getContextNames).disconnect();
List contextInfos = new ArrayList<>();
for (String contextName : contextNames) {
URLConnection getContextInfo = new URL(
"http://" + System.getProperty("cluster.uri") + "/_scripts/painless/_context?context=" + contextName
).openConnection();
parser = JsonXContent.jsonXContent.createParser(null, null, getContextInfo.getInputStream());
contextInfos.add(PainlessContextInfo.fromXContent(parser));
((HttpURLConnection) getContextInfo).disconnect();
}
contextInfos.sort(Comparator.comparing(PainlessContextInfo::getName));
return contextInfos;
}
private static Set createSharedStatics(List contextInfos) {
Map staticInfoCounts = new HashMap<>();
for (PainlessContextInfo contextInfo : contextInfos) {
for (PainlessContextMethodInfo methodInfo : contextInfo.getImportedMethods()) {
staticInfoCounts.merge(methodInfo, 1, Integer::sum);
}
for (PainlessContextClassBindingInfo classBindingInfo : contextInfo.getClassBindings()) {
staticInfoCounts.merge(classBindingInfo, 1, Integer::sum);
}
for (PainlessContextInstanceBindingInfo instanceBindingInfo : contextInfo.getInstanceBindings()) {
staticInfoCounts.merge(instanceBindingInfo, 1, Integer::sum);
}
}
return staticInfoCounts.entrySet()
.stream()
.filter(e -> e.getValue() == contextInfos.size())
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
}
private static List createContextStatics(PainlessContextInfo contextInfo) {
List staticInfos = new ArrayList<>();
staticInfos.addAll(contextInfo.getImportedMethods());
staticInfos.addAll(contextInfo.getClassBindings());
staticInfos.addAll(contextInfo.getInstanceBindings());
return staticInfos;
}
private static Set createSharedClasses(List contextInfos) {
Map classInfoCounts = new HashMap<>();
for (PainlessContextInfo contextInfo : contextInfos) {
for (PainlessContextClassInfo classInfo : contextInfo.getClasses()) {
classInfoCounts.merge(classInfo, 1, Integer::sum);
}
}
return classInfoCounts.entrySet()
.stream()
.filter(e -> e.getValue() == contextInfos.size())
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
}
@SuppressForbidden(reason = "resolve api docs directory with environment")
private static Path resetRootDir() throws IOException {
Path rootDir = PathUtils.get("../../docs/painless/painless-api-reference");
IOUtils.rm(rootDir);
Files.createDirectories(rootDir);
return rootDir;
}
private static Path createSharedDir(Path rootDir) throws IOException {
Path sharedDir = rootDir.resolve(SHARED_HEADER);
Files.createDirectories(sharedDir);
return sharedDir;
}
private static Path createContextDir(Path rootDir, PainlessContextInfo info) throws IOException {
Path contextDir = rootDir.resolve(getContextHeader(info));
Files.createDirectories(contextDir);
return contextDir;
}
private static void printAutomatedMessage(PrintStream stream) {
stream.println("// This file is auto-generated. Do not edit.");
stream.println();
}
private static void printSharedIndexPage(
Path sharedDir,
Map javaNamesToDisplayNames,
List staticInfos,
List classInfos
) throws IOException {
Path sharedIndexPath = sharedDir.resolve("index.asciidoc");
try (
PrintStream sharedIndexStream = new PrintStream(
Files.newOutputStream(sharedIndexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
false,
StandardCharsets.UTF_8.name()
)
) {
printAutomatedMessage(sharedIndexStream);
sharedIndexStream.println("[[" + SHARED_HEADER + "]]");
sharedIndexStream.println("=== " + SHARED_NAME + " API");
sharedIndexStream.println();
sharedIndexStream.println("The following API is available in all contexts.");
printIndex(sharedIndexStream, SHARED_HEADER, javaNamesToDisplayNames, staticInfos, classInfos);
}
}
private static void printContextIndexPage(
Path contextDir,
Map javaNamesToDisplayNames,
PainlessContextInfo contextInfo,
List staticInfos,
List classInfos
) throws IOException {
Path contextIndexPath = contextDir.resolve("index.asciidoc");
try (
PrintStream contextIndexStream = new PrintStream(
Files.newOutputStream(contextIndexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
false,
StandardCharsets.UTF_8.name()
)
) {
printAutomatedMessage(contextIndexStream);
contextIndexStream.println("[[" + getContextHeader(contextInfo) + "]]");
contextIndexStream.println("=== " + getContextName(contextInfo) + " API");
contextIndexStream.println();
contextIndexStream.println("The following specialized API is available in the " + getContextName(contextInfo) + " context.");
contextIndexStream.println();
contextIndexStream.println(
"* See the <<" + SHARED_HEADER + ", " + SHARED_NAME + " API>> for further API available in all contexts."
);
printIndex(contextIndexStream, getContextHeader(contextInfo), javaNamesToDisplayNames, staticInfos, classInfos);
}
}
private static void printIndex(
PrintStream indexStream,
String contextHeader,
Map javaNamesToDisplayNames,
List staticInfos,
List classInfos
) {
String currentPackageName = null;
if (staticInfos.isEmpty() == false) {
indexStream.println();
indexStream.println("==== Static Methods");
indexStream.println(
"The following methods are directly callable without a class/instance qualifier. "
+ "Note parameters denoted by a (*) are treated as read-only values."
);
indexStream.println();
for (Object staticInfo : staticInfos) {
if (staticInfo instanceof PainlessContextMethodInfo) {
printMethod(indexStream, javaNamesToDisplayNames, false, (PainlessContextMethodInfo) staticInfo);
} else if (staticInfo instanceof PainlessContextClassBindingInfo) {
printClassBinding(indexStream, javaNamesToDisplayNames, (PainlessContextClassBindingInfo) staticInfo);
} else if (staticInfo instanceof PainlessContextInstanceBindingInfo) {
printInstanceBinding(indexStream, javaNamesToDisplayNames, (PainlessContextInstanceBindingInfo) staticInfo);
} else {
throw new IllegalArgumentException("unexpected static info type");
}
}
}
if (classInfos.isEmpty() == false) {
indexStream.println();
indexStream.println("==== Classes By Package");
indexStream.println(
"The following classes are available grouped by their respective packages. Click on a class "
+ "to view details about the available methods and fields."
);
indexStream.println();
for (PainlessContextClassInfo classInfo : classInfos) {
String classPackageName = classInfo.getName().substring(0, classInfo.getName().lastIndexOf('.'));
if (classPackageName.equals(currentPackageName) == false) {
currentPackageName = classPackageName;
indexStream.println();
indexStream.println("==== " + currentPackageName);
indexStream.println(
"<<"
+ getPackageHeader(contextHeader, currentPackageName)
+ ", "
+ "Expand details for "
+ currentPackageName
+ ">>"
);
indexStream.println();
}
String className = getType(javaNamesToDisplayNames, classInfo.getName());
indexStream.println("* <<" + getClassHeader(contextHeader, className) + ", " + className + ">>");
}
}
indexStream.println();
indexStream.println("include::packages.asciidoc[]");
indexStream.println();
}
private static void printSharedPackagesPages(
Path sharedDir,
Map javaNamesToDisplayNames,
List classInfos
) throws IOException {
Path sharedClassesPath = sharedDir.resolve("packages.asciidoc");
try (
PrintStream sharedPackagesStream = new PrintStream(
Files.newOutputStream(sharedClassesPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
false,
StandardCharsets.UTF_8.name()
)
) {
printAutomatedMessage(sharedPackagesStream);
printPackages(sharedPackagesStream, SHARED_NAME, SHARED_HEADER, javaNamesToDisplayNames, Collections.emptySet(), classInfos);
}
}
private static void printContextPackagesPages(
Path contextDir,
Map javaNamesToDisplayNames,
Set excludes,
PainlessContextInfo contextInfo,
List classInfos
) throws IOException {
Path contextPackagesPath = contextDir.resolve("packages.asciidoc");
try (
PrintStream contextPackagesStream = new PrintStream(
Files.newOutputStream(contextPackagesPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
false,
StandardCharsets.UTF_8.name()
)
) {
printAutomatedMessage(contextPackagesStream);
printPackages(
contextPackagesStream,
getContextName(contextInfo),
getContextHeader(contextInfo),
javaNamesToDisplayNames,
excludes,
classInfos
);
}
}
private static void printPackages(
PrintStream packagesStream,
String contextName,
String contextHeader,
Map javaNamesToDisplayNames,
Set excludes,
List classInfos
) {
String currentPackageName = null;
for (PainlessContextClassInfo classInfo : classInfos) {
if (excludes.contains(classInfo)) {
continue;
}
String classPackageName = classInfo.getName().substring(0, classInfo.getName().lastIndexOf('.'));
if (classPackageName.equals(currentPackageName) == false) {
currentPackageName = classPackageName;
packagesStream.println();
packagesStream.println("[role=\"exclude\",id=\"" + getPackageHeader(contextHeader, currentPackageName) + "\"]");
packagesStream.println("=== " + contextName + " API for package " + currentPackageName);
packagesStream.println(
"See the <<" + contextHeader + ", " + contextName + " API>> " + "for a high-level overview of all packages and classes."
);
}
String className = getType(javaNamesToDisplayNames, classInfo.getName());
packagesStream.println();
packagesStream.println("[[" + getClassHeader(contextHeader, className) + "]]");
packagesStream.println("==== " + className + "");
for (PainlessContextFieldInfo fieldInfo : classInfo.getStaticFields()) {
printField(packagesStream, javaNamesToDisplayNames, true, fieldInfo);
}
for (PainlessContextMethodInfo methodInfo : classInfo.getStaticMethods()) {
printMethod(packagesStream, javaNamesToDisplayNames, true, methodInfo);
}
for (PainlessContextFieldInfo fieldInfo : classInfo.getFields()) {
printField(packagesStream, javaNamesToDisplayNames, false, fieldInfo);
}
for (PainlessContextConstructorInfo constructorInfo : classInfo.getConstructors()) {
printConstructor(packagesStream, javaNamesToDisplayNames, className, constructorInfo);
}
for (PainlessContextMethodInfo methodInfo : classInfo.getMethods()) {
printMethod(packagesStream, javaNamesToDisplayNames, false, methodInfo);
}
packagesStream.println();
}
packagesStream.println();
}
private static void printRootIndexPage(Path rootDir, List contextInfos, Set isSpecialized)
throws IOException {
Path rootIndexPath = rootDir.resolve("index.asciidoc");
try (
PrintStream rootIndexStream = new PrintStream(
Files.newOutputStream(rootIndexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
false,
StandardCharsets.UTF_8.name()
)
) {
printAutomatedMessage(rootIndexStream);
rootIndexStream.println("[cols=\"<3,^3,^3\"]");
rootIndexStream.println("|====");
for (PainlessContextInfo contextInfo : contextInfos) {
String contextName = getContextName(contextInfo);
String contextHeader = getContextHeader(contextInfo);
rootIndexStream.print("|" + contextName + " ");
rootIndexStream.print("| <<" + SHARED_HEADER + ", " + SHARED_NAME + " API>> ");
if (isSpecialized.contains(contextInfo)) {
rootIndexStream.println("| <<" + contextHeader + ", Specialized API>>");
} else {
rootIndexStream.println("| ");
}
}
rootIndexStream.println("|====");
rootIndexStream.println();
rootIndexStream.println("include::" + SHARED_HEADER + "/index.asciidoc[]");
for (PainlessContextInfo contextInfo : contextInfos) {
if (isSpecialized.contains(contextInfo)) {
rootIndexStream.println("include::" + getContextHeader(contextInfo) + "/index.asciidoc[]");
}
}
}
}
private static void printConstructor(
PrintStream stream,
Map javaNamesToDisplayNames,
String className,
PainlessContextConstructorInfo constructorInfo
) {
stream.print("* ");
if (constructorInfo.getDeclaring().startsWith("java.")) {
stream.print(getConstructorJavaDocLink(constructorInfo) + "[" + className + "]");
} else {
stream.print(className);
}
stream.print("(");
for (int parameterIndex = 0; parameterIndex < constructorInfo.getParameters().size(); ++parameterIndex) {
stream.print(getType(javaNamesToDisplayNames, constructorInfo.getParameters().get(parameterIndex)));
if (parameterIndex + 1 < constructorInfo.getParameters().size()) {
stream.print(", ");
}
}
stream.println(")");
}
private static void printMethod(
PrintStream stream,
Map javaNamesToDisplayNames,
boolean isStatic,
PainlessContextMethodInfo methodInfo
) {
stream.print("* " + (isStatic ? "static " : ""));
stream.print(getType(javaNamesToDisplayNames, methodInfo.getRtn()) + " ");
if (methodInfo.getDeclaring().startsWith("java.")) {
stream.print(getMethodJavaDocLink(methodInfo) + "[" + methodInfo.getName() + "]");
} else {
stream.print(methodInfo.getName());
}
stream.print("(");
for (int parameterIndex = 0; parameterIndex < methodInfo.getParameters().size(); ++parameterIndex) {
stream.print(getType(javaNamesToDisplayNames, methodInfo.getParameters().get(parameterIndex)));
if (parameterIndex + 1 < methodInfo.getParameters().size()) {
stream.print(", ");
}
}
stream.println(")");
}
private static void printClassBinding(
PrintStream stream,
Map javaNamesToDisplayNames,
PainlessContextClassBindingInfo classBindingInfo
) {
stream.print("* " + getType(javaNamesToDisplayNames, classBindingInfo.getRtn()) + " " + classBindingInfo.getName() + "(");
for (int parameterIndex = 0; parameterIndex < classBindingInfo.getParameters().size(); ++parameterIndex) {
// temporary fix to not print org.opensearch.script.ScoreScript parameter until
// class instance bindings are created and the information is appropriately added to the context info classes
if ("org.opensearch.script.ScoreScript".equals(
getType(javaNamesToDisplayNames, classBindingInfo.getParameters().get(parameterIndex))
)) {
continue;
}
stream.print(getType(javaNamesToDisplayNames, classBindingInfo.getParameters().get(parameterIndex)));
if (parameterIndex < classBindingInfo.getReadOnly()) {
stream.print(" *");
}
if (parameterIndex + 1 < classBindingInfo.getParameters().size()) {
stream.print(", ");
}
}
stream.println(")");
}
private static void printInstanceBinding(
PrintStream stream,
Map javaNamesToDisplayNames,
PainlessContextInstanceBindingInfo instanceBindingInfo
) {
stream.print("* " + getType(javaNamesToDisplayNames, instanceBindingInfo.getRtn()) + " " + instanceBindingInfo.getName() + "(");
for (int parameterIndex = 0; parameterIndex < instanceBindingInfo.getParameters().size(); ++parameterIndex) {
stream.print(getType(javaNamesToDisplayNames, instanceBindingInfo.getParameters().get(parameterIndex)));
if (parameterIndex + 1 < instanceBindingInfo.getParameters().size()) {
stream.print(", ");
}
}
stream.println(")");
}
private static void printField(
PrintStream stream,
Map javaNamesToDisplayNames,
boolean isStatic,
PainlessContextFieldInfo fieldInfo
) {
stream.print("* " + (isStatic ? "static " : ""));
stream.print(getType(javaNamesToDisplayNames, fieldInfo.getType()) + " ");
if (fieldInfo.getDeclaring().startsWith("java.")) {
stream.println(getFieldJavaDocLink(fieldInfo) + "[" + fieldInfo.getName() + "]");
} else {
stream.println(fieldInfo.getName());
}
}
private static String getType(Map javaNamesToDisplayNames, String javaType) {
int arrayDimensions = 0;
while (javaType.charAt(arrayDimensions) == '[') {
++arrayDimensions;
}
if (arrayDimensions > 0) {
if (javaType.charAt(javaType.length() - 1) == ';') {
javaType = javaType.substring(arrayDimensions + 1, javaType.length() - 1);
} else {
javaType = javaType.substring(arrayDimensions);
}
}
if ("Z".equals(javaType) || "boolean".equals(javaType)) {
javaType = "boolean";
} else if ("V".equals(javaType) || "void".equals(javaType)) {
javaType = "void";
} else if ("B".equals(javaType) || "byte".equals(javaType)) {
javaType = "byte";
} else if ("S".equals(javaType) || "short".equals(javaType)) {
javaType = "short";
} else if ("C".equals(javaType) || "char".equals(javaType)) {
javaType = "char";
} else if ("I".equals(javaType) || "int".equals(javaType)) {
javaType = "int";
} else if ("J".equals(javaType) || "long".equals(javaType)) {
javaType = "long";
} else if ("F".equals(javaType) || "float".equals(javaType)) {
javaType = "float";
} else if ("D".equals(javaType) || "double".equals(javaType)) {
javaType = "double";
} else if ("org.opensearch.painless.lookup.def".equals(javaType)) {
javaType = "def";
} else {
javaType = javaNamesToDisplayNames.get(javaType);
}
while (arrayDimensions-- > 0) {
javaType += "[]";
}
return javaType;
}
private static String getFieldJavaDocLink(PainlessContextFieldInfo fieldInfo) {
return "{java11-javadoc}/java.base/" + fieldInfo.getDeclaring().replace('.', '/') + ".html#" + fieldInfo.getName();
}
private static String getConstructorJavaDocLink(PainlessContextConstructorInfo constructorInfo) {
StringBuilder javaDocLink = new StringBuilder();
javaDocLink.append("{java11-javadoc}/java.base/");
javaDocLink.append(constructorInfo.getDeclaring().replace('.', '/'));
javaDocLink.append(".html#(");
for (int parameterIndex = 0; parameterIndex < constructorInfo.getParameters().size(); ++parameterIndex) {
javaDocLink.append(getLinkType(constructorInfo.getParameters().get(parameterIndex)));
if (parameterIndex + 1 < constructorInfo.getParameters().size()) {
javaDocLink.append(",");
}
}
javaDocLink.append(")");
return javaDocLink.toString();
}
private static String getMethodJavaDocLink(PainlessContextMethodInfo methodInfo) {
StringBuilder javaDocLink = new StringBuilder();
javaDocLink.append("{java11-javadoc}/java.base/");
javaDocLink.append(methodInfo.getDeclaring().replace('.', '/'));
javaDocLink.append(".html#");
javaDocLink.append(methodInfo.getName());
javaDocLink.append("(");
for (int parameterIndex = 0; parameterIndex < methodInfo.getParameters().size(); ++parameterIndex) {
javaDocLink.append(getLinkType(methodInfo.getParameters().get(parameterIndex)));
if (parameterIndex + 1 < methodInfo.getParameters().size()) {
javaDocLink.append(",");
}
}
javaDocLink.append(")");
return javaDocLink.toString();
}
private static String getLinkType(String javaType) {
int arrayDimensions = 0;
while (javaType.charAt(arrayDimensions) == '[') {
++arrayDimensions;
}
if (arrayDimensions > 0) {
if (javaType.charAt(javaType.length() - 1) == ';') {
javaType = javaType.substring(arrayDimensions + 1, javaType.length() - 1);
} else {
javaType = javaType.substring(arrayDimensions);
}
}
if ("Z".equals(javaType) || "boolean".equals(javaType)) {
javaType = "boolean";
} else if ("V".equals(javaType) || "void".equals(javaType)) {
javaType = "void";
} else if ("B".equals(javaType) || "byte".equals(javaType)) {
javaType = "byte";
} else if ("S".equals(javaType) || "short".equals(javaType)) {
javaType = "short";
} else if ("C".equals(javaType) || "char".equals(javaType)) {
javaType = "char";
} else if ("I".equals(javaType) || "int".equals(javaType)) {
javaType = "int";
} else if ("J".equals(javaType) || "long".equals(javaType)) {
javaType = "long";
} else if ("F".equals(javaType) || "float".equals(javaType)) {
javaType = "float";
} else if ("D".equals(javaType) || "double".equals(javaType)) {
javaType = "double";
} else if ("org.opensearch.painless.lookup.def".equals(javaType)) {
javaType = "java.lang.Object";
}
while (arrayDimensions-- > 0) {
javaType += "%5B%5D";
}
return javaType;
}
private static String getContextHeader(PainlessContextInfo contextInfo) {
return "painless-api-reference-" + contextInfo.getName().replace(" ", "-").replace("_", "-");
}
private static String getPackageHeader(String contextHeader, String packageName) {
return contextHeader + "-" + packageName.replace('.', '-');
}
private static String getClassHeader(String contextHeader, String className) {
return contextHeader + "-" + className.replace('.', '-');
}
private static String getContextName(PainlessContextInfo contextInfo) {
String[] split = contextInfo.getName().split("[_-]");
StringBuilder contextNameBuilder = new StringBuilder();
for (String part : split) {
contextNameBuilder.append(Character.toUpperCase(part.charAt(0)));
contextNameBuilder.append(part.substring(1));
contextNameBuilder.append(' ');
}
return contextNameBuilder.substring(0, contextNameBuilder.length() - 1);
}
private static List sortStaticInfos(Set staticExcludes, List staticInfos) {
staticInfos = new ArrayList<>(staticInfos);
staticInfos.removeIf(staticExcludes::contains);
staticInfos.sort((si1, si2) -> {
String sv1;
String sv2;
if (si1 instanceof PainlessContextMethodInfo) {
sv1 = ((PainlessContextMethodInfo) si1).getSortValue();
} else if (si1 instanceof PainlessContextClassBindingInfo) {
sv1 = ((PainlessContextClassBindingInfo) si1).getSortValue();
} else if (si1 instanceof PainlessContextInstanceBindingInfo) {
sv1 = ((PainlessContextInstanceBindingInfo) si1).getSortValue();
} else {
throw new IllegalArgumentException("unexpected static info type");
}
if (si2 instanceof PainlessContextMethodInfo) {
sv2 = ((PainlessContextMethodInfo) si2).getSortValue();
} else if (si2 instanceof PainlessContextClassBindingInfo) {
sv2 = ((PainlessContextClassBindingInfo) si2).getSortValue();
} else if (si2 instanceof PainlessContextInstanceBindingInfo) {
sv2 = ((PainlessContextInstanceBindingInfo) si2).getSortValue();
} else {
throw new IllegalArgumentException("unexpected static info type");
}
return sv1.compareTo(sv2);
});
return staticInfos;
}
private static List sortClassInfos(
Set classExcludes,
List classInfos
) {
classInfos = new ArrayList<>(classInfos);
classInfos.removeIf(
v -> "void".equals(v.getName())
|| "boolean".equals(v.getName())
|| "byte".equals(v.getName())
|| "short".equals(v.getName())
|| "char".equals(v.getName())
|| "int".equals(v.getName())
|| "long".equals(v.getName())
|| "float".equals(v.getName())
|| "double".equals(v.getName())
|| "org.opensearch.painless.lookup.def".equals(v.getName())
|| isInternalClass(v.getName())
|| classExcludes.contains(v)
);
classInfos.sort((c1, c2) -> {
String n1 = c1.getName();
String n2 = c2.getName();
boolean i1 = c1.isImported();
boolean i2 = c2.isImported();
String p1 = n1.substring(0, n1.lastIndexOf('.'));
String p2 = n2.substring(0, n2.lastIndexOf('.'));
int compare = p1.compareTo(p2);
if (compare == 0) {
if (i1 && i2) {
compare = n1.substring(n1.lastIndexOf('.') + 1).compareTo(n2.substring(n2.lastIndexOf('.') + 1));
} else if (i1 == false && i2 == false) {
compare = n1.compareTo(n2);
} else {
compare = Boolean.compare(i1, i2) * -1;
}
}
return compare;
});
return classInfos;
}
private static Map getDisplayNames(List classInfos) {
Map javaNamesToDisplayNames = new HashMap<>();
for (PainlessContextClassInfo classInfo : classInfos) {
String className = classInfo.getName();
if (classInfo.isImported()) {
javaNamesToDisplayNames.put(className, className.substring(className.lastIndexOf('.') + 1).replace('$', '.'));
} else {
javaNamesToDisplayNames.put(className, className.replace('$', '.'));
}
}
return javaNamesToDisplayNames;
}
private static boolean isInternalClass(String javaName) {
return javaName.equals("org.opensearch.script.ScoreScript")
|| javaName.equals("org.opensearch.script.ScoreScript$ExplanationHolder");
}
private ContextDocGenerator() {
}
}