Merge branch 'master' into ccr
* master: [TEST] Mute SlackMessageTests.testTemplateRender Docs: Explain closing the high level client [ML] Re-enable memory limit integration tests (#31328) [test] disable packaging tests for suse boxes Add nio transport to security plugin (#31942) XContentTests : Insert random fields at random positions (#30867) Force execution of fetch tasks (#31974) Fix unreachable error condition in AmazonS3Fixture (#32005) Tests: Fix SearchFieldsIT.testDocValueFields (#31995) Add Expected Reciprocal Rank metric (#31891) [ML] Get ForecastRequestStats doc in RestoreModelSnapshotIT (#31973) SQL: Add support for single parameter text manipulating functions (#31874) [ML] Ensure immutability of MlMetadata (#31957) Tests: Mute SearchFieldsIT.testDocValueFields() muted tests due to #31940 Work around reported problem in eclipse (#31960) Move build integration tests out of :buildSrc project (#31961) Tests: Remove use of joda time in some tests (#31922) [Test] Reactive 3rd party tests on CI (#31919) SQL: Support for escape sequences (#31884) SQL: HAVING clause should accept only aggregates (#31872) Docs: fix typo in datehistogram (#31972) Switch url repository rest tests to new style requests (#31944) Switch reindex tests to new style requests (#31941) Docs: Added note about cloud service to installation and getting started [DOCS] Removes alternative docker pull example (#31934) Add Snapshots Status API to High Level Rest Client (#31515) ingest: date_index_name processor template resolution (#31841) Test: fix null failure in watcher test (#31968) Switch test framework to new style requests (#31939) Switch low level rest tests to new style Requests (#31938) Switch high level rest tests to new style requests (#31937) [ML] Mute test failing due to Java 11 date time format parsing bug (#31899) [TEST] Mute SlackMessageTests.testTemplateRender Fix assertIngestDocument wrongfully passing (#31913) Remove unused reference to filePermissionsCache (#31923) rolling upgrade should use a replica to prevent relocations while running a scroll HLREST: Bundle the x-pack protocol project (#31904) Increase logging level for testStressMaybeFlush Added lenient flag for synonym token filter (#31484) [X-Pack] Beats centralized management: security role + licensing (#30520) HLRest: Move xPackInfo() to xPack().info() (#31905) Docs: add security delete role to api call table (#31907) [test] port archive distribution packaging tests (#31314) Watcher: Slack message empty text (#31596) [ML] Mute failing DetectionRulesIT.testCondition() test Fix broken NaN check in MovingFunctions#stdDev() (#31888) Date: Add DateFormatters class that uses java.time (#31856) [ML] Switch native QA tests to a 3 node cluster (#31757) Change trappy float comparison (#31889) Fix building AD URL from domain name (#31849) Add opaque_id to audit logging (#31878) re-enable backcompat tests add support for is_write_index in put-alias body parsing (#31674) Improve release notes script (#31833) [DOCS] Fix broken link in painless example Handle missing values in painless (#30975) Remove the ability to index or query context suggestions without context (#31007) Ingest: Enable Templated Fieldnames in Rename (#31690) [Docs] Fix typo in the Rollup API Quick Reference (#31855) Ingest: Add ignore_missing option to RemoveProc (#31693) Add template config for Beat state to X-Pack Monitoring (#31809) Watcher: Add ssl.trust email account setting (#31684) Remove link to oss-MSI (#31844) Painless: Restructure Definition/Whitelist (#31879) HLREST: Add x-pack-info API (#31870)
This commit is contained in:
commit
b1f5d361b3
|
@ -435,6 +435,9 @@ allprojects {
|
|||
if (isEclipse) {
|
||||
// set this so generated dirs will be relative to eclipse build
|
||||
project.buildDir = eclipseBuild
|
||||
// Work around https://docs.gradle.org/current/userguide/java_gradle_plugin.html confusing Eclipse by the metadata
|
||||
// it adds to the classpath
|
||||
project.file("$buildDir/pluginUnderTestMetadata").mkdirs()
|
||||
}
|
||||
eclipse.classpath.file.whenMerged { classpath ->
|
||||
// give each source folder a unique corresponding output folder
|
||||
|
|
|
@ -128,6 +128,10 @@ if (project == rootProject) {
|
|||
}
|
||||
mavenCentral()
|
||||
}
|
||||
test {
|
||||
include "**/*Tests.class"
|
||||
exclude "**/*IT.class"
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
@ -152,6 +156,18 @@ if (project != rootProject) {
|
|||
jarHell.enabled = false
|
||||
thirdPartyAudit.enabled = false
|
||||
|
||||
// tests can't be run with randomized test runner
|
||||
// it's fine as we run them as part of :buildSrc
|
||||
test.enabled = false
|
||||
task integTest(type: Test) {
|
||||
exclude "**/*Tests.class"
|
||||
include "**/*IT.class"
|
||||
testClassesDirs = sourceSets.test.output.classesDirs
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
inputs.dir(file("src/testKit"))
|
||||
}
|
||||
check.dependsOn(integTest)
|
||||
|
||||
// TODO: re-enable once randomizedtesting gradle code is published and removed from here
|
||||
licenseHeaders.enabled = false
|
||||
|
||||
|
|
|
@ -691,6 +691,7 @@ class BuildPlugin implements Plugin<Project> {
|
|||
systemProperty 'tests.task', path
|
||||
systemProperty 'tests.security.manager', 'true'
|
||||
systemProperty 'jna.nosys', 'true'
|
||||
systemProperty 'es.scripting.exception_for_missing_value', 'true'
|
||||
// TODO: remove setting logging level via system property
|
||||
systemProperty 'tests.logger.level', 'WARN'
|
||||
for (Map.Entry<String, String> property : System.properties.entrySet()) {
|
||||
|
|
|
@ -526,7 +526,11 @@ class VagrantTestPlugin implements Plugin<Project> {
|
|||
project.gradle.removeListener(batsPackagingReproListener)
|
||||
}
|
||||
if (project.extensions.esvagrant.boxes.contains(box)) {
|
||||
packagingTest.dependsOn(batsPackagingTest)
|
||||
// these tests are temporarily disabled for suse boxes while we debug an issue
|
||||
// https://github.com/elastic/elasticsearch/issues/30295
|
||||
if (box.equals("opensuse-42") == false && box.equals("sles-12") == false) {
|
||||
packagingTest.dependsOn(batsPackagingTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -546,9 +550,15 @@ class VagrantTestPlugin implements Plugin<Project> {
|
|||
javaPackagingTest.command = 'ssh'
|
||||
javaPackagingTest.args = ['--command', 'sudo bash "$PACKAGING_TESTS/run-tests.sh"']
|
||||
} else {
|
||||
// powershell sessions run over winrm always run as administrator, whether --elevated is passed or not. however
|
||||
// remote sessions have some restrictions on what they can do, such as impersonating another user (or the same user
|
||||
// without administrator elevation), which we need to do for these tests. passing --elevated runs the session
|
||||
// as a scheduled job locally on the vm as a true administrator to get around this limitation
|
||||
//
|
||||
// https://github.com/hashicorp/vagrant/blob/9c299a2a357fcf87f356bb9d56e18a037a53d138/plugins/communicators/winrm/communicator.rb#L195-L225
|
||||
// https://devops-collective-inc.gitbooks.io/secrets-of-powershell-remoting/content/manuscript/accessing-remote-computers.html
|
||||
javaPackagingTest.command = 'winrm'
|
||||
// winrm commands run as administrator
|
||||
javaPackagingTest.args = ['--command', 'powershell -File "$Env:PACKAGING_TESTS/run-tests.ps1"']
|
||||
javaPackagingTest.args = ['--elevated', '--command', 'powershell -File "$Env:PACKAGING_TESTS/run-tests.ps1"']
|
||||
}
|
||||
|
||||
TaskExecutionAdapter javaPackagingReproListener = createReproListener(project, javaPackagingTest.path)
|
||||
|
@ -559,7 +569,11 @@ class VagrantTestPlugin implements Plugin<Project> {
|
|||
project.gradle.removeListener(javaPackagingReproListener)
|
||||
}
|
||||
if (project.extensions.esvagrant.boxes.contains(box)) {
|
||||
packagingTest.dependsOn(javaPackagingTest)
|
||||
// these tests are temporarily disabled for suse boxes while we debug an issue
|
||||
// https://github.com/elastic/elasticsearch/issues/30295
|
||||
if (box.equals("opensuse-42") == false && box.equals("sles-12") == false) {
|
||||
packagingTest.dependsOn(javaPackagingTest)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import org.elasticsearch.gradle.precommit.PrecommitTasks
|
||||
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
|
@ -18,29 +16,86 @@ import org.elasticsearch.gradle.precommit.PrecommitTasks
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import org.elasticsearch.gradle.precommit.PrecommitTasks
|
||||
import org.gradle.api.XmlProvider
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://plugins.gradle.org/m2/'
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'elasticsearch.build'
|
||||
apply plugin: 'elasticsearch.rest-test'
|
||||
apply plugin: 'nebula.maven-base-publish'
|
||||
apply plugin: 'nebula.maven-scm'
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
|
||||
group = 'org.elasticsearch.client'
|
||||
archivesBaseName = 'elasticsearch-rest-high-level-client'
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
nebula {
|
||||
artifactId = archivesBaseName
|
||||
publications {
|
||||
nebula(MavenPublication) {
|
||||
artifact shadowJar
|
||||
artifactId = archivesBaseName
|
||||
/*
|
||||
* Configure the pom to include the "shadow" as compile dependencies
|
||||
* because that is how we're using them but remove all other dependencies
|
||||
* because they've been shaded into the jar.
|
||||
*/
|
||||
pom.withXml { XmlProvider xml ->
|
||||
Node root = xml.asNode()
|
||||
root.remove(root.dependencies)
|
||||
Node dependenciesNode = root.appendNode('dependencies')
|
||||
project.configurations.shadow.allDependencies.each {
|
||||
if (false == it instanceof SelfResolvingDependency) {
|
||||
Node dependencyNode = dependenciesNode.appendNode('dependency')
|
||||
dependencyNode.appendNode('groupId', it.group)
|
||||
dependencyNode.appendNode('artifactId', it.name)
|
||||
dependencyNode.appendNode('version', it.version)
|
||||
dependencyNode.appendNode('scope', 'compile')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We need somewhere to configure dependencies that we don't wish to shade
|
||||
* into the high level REST client. The shadow plugin creates a "shadow"
|
||||
* configuration which is *almost* exactly that. It is never bundled into
|
||||
* the shaded jar but is used for main source compilation. Unfortunately,
|
||||
* by default it is not used for *test* source compilation and isn't used
|
||||
* in tests at all. This change makes it available for test compilation.
|
||||
* A change below makes it available for testing.
|
||||
*/
|
||||
sourceSets {
|
||||
test {
|
||||
compileClasspath += configurations.shadow
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.elasticsearch:elasticsearch:${version}"
|
||||
compile "org.elasticsearch.client:elasticsearch-rest-client:${version}"
|
||||
compile "org.elasticsearch.plugin:parent-join-client:${version}"
|
||||
compile "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}"
|
||||
compile "org.elasticsearch.plugin:rank-eval-client:${version}"
|
||||
compile "org.elasticsearch.plugin:lang-mustache-client:${version}"
|
||||
/*
|
||||
* Everything in the "shadow" configuration is *not* copied into the
|
||||
* shadowJar.
|
||||
*/
|
||||
shadow "org.elasticsearch:elasticsearch:${version}"
|
||||
shadow "org.elasticsearch.client:elasticsearch-rest-client:${version}"
|
||||
shadow "org.elasticsearch.plugin:parent-join-client:${version}"
|
||||
shadow "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}"
|
||||
shadow "org.elasticsearch.plugin:rank-eval-client:${version}"
|
||||
shadow "org.elasticsearch.plugin:lang-mustache-client:${version}"
|
||||
compile project(':x-pack:protocol')
|
||||
|
||||
testCompile "org.elasticsearch.client:test:${version}"
|
||||
testCompile "org.elasticsearch.test:framework:${version}"
|
||||
|
@ -63,3 +118,48 @@ forbiddenApisMain {
|
|||
signaturesURLs += [PrecommitTasks.getResource('/forbidden/http-signatures.txt')]
|
||||
signaturesURLs += [file('src/main/resources/forbidden/rest-high-level-signatures.txt').toURI().toURL()]
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
classifier = null
|
||||
mergeServiceFiles()
|
||||
}
|
||||
|
||||
// We don't need normal jar, we use shadow jar instead
|
||||
jar.enabled = false
|
||||
assemble.dependsOn shadowJar
|
||||
|
||||
javadoc {
|
||||
/*
|
||||
* Bundle all of the javadoc from all of the shaded projects into this one
|
||||
* so we don't *have* to publish javadoc for all of the "client" jars.
|
||||
*/
|
||||
configurations.compile.dependencies.all { Dependency dep ->
|
||||
Project p = dependencyToProject(dep)
|
||||
if (p != null) {
|
||||
evaluationDependsOn(p.path)
|
||||
source += p.sourceSets.main.allJava
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the jar for testing so we have tests of the bundled jar.
|
||||
* Use the "shadow" configuration for testing because we need things
|
||||
* in it.
|
||||
*/
|
||||
test {
|
||||
classpath -= compileJava.outputs.files
|
||||
classpath -= configurations.compile
|
||||
classpath -= configurations.runtime
|
||||
classpath += configurations.shadow
|
||||
classpath += shadowJar.outputs.files
|
||||
dependsOn shadowJar
|
||||
}
|
||||
integTestRunner {
|
||||
classpath -= compileJava.outputs.files
|
||||
classpath -= configurations.compile
|
||||
classpath -= configurations.runtime
|
||||
classpath += configurations.shadow
|
||||
classpath += shadowJar.outputs.files
|
||||
dependsOn shadowJar
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
|
|||
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
|
||||
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
|
||||
|
@ -104,6 +105,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
|||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.VersionType;
|
||||
import org.elasticsearch.index.rankeval.RankEvalRequest;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
|
||||
import org.elasticsearch.rest.action.search.RestSearchAction;
|
||||
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
|
||||
import org.elasticsearch.script.mustache.SearchTemplateRequest;
|
||||
|
@ -115,8 +117,10 @@ import java.io.IOException;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
final class RequestConverters {
|
||||
static final XContentType REQUEST_BODY_CONTENT_TYPE = XContentType.JSON;
|
||||
|
@ -960,6 +964,20 @@ final class RequestConverters {
|
|||
return request;
|
||||
}
|
||||
|
||||
static Request snapshotsStatus(SnapshotsStatusRequest snapshotsStatusRequest) {
|
||||
String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot")
|
||||
.addPathPart(snapshotsStatusRequest.repository())
|
||||
.addCommaSeparatedPathParts(snapshotsStatusRequest.snapshots())
|
||||
.addPathPartAsIs("_status")
|
||||
.build();
|
||||
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
|
||||
|
||||
Params parameters = new Params(request);
|
||||
parameters.withMasterTimeout(snapshotsStatusRequest.masterNodeTimeout());
|
||||
parameters.withIgnoreUnavailable(snapshotsStatusRequest.ignoreUnavailable());
|
||||
return request;
|
||||
}
|
||||
|
||||
static Request deleteSnapshot(DeleteSnapshotRequest deleteSnapshotRequest) {
|
||||
String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot")
|
||||
.addPathPart(deleteSnapshotRequest.repository())
|
||||
|
@ -1065,6 +1083,19 @@ final class RequestConverters {
|
|||
return request;
|
||||
}
|
||||
|
||||
static Request xPackInfo(XPackInfoRequest infoRequest) {
|
||||
Request request = new Request(HttpGet.METHOD_NAME, "/_xpack");
|
||||
if (false == infoRequest.isVerbose()) {
|
||||
request.addParameter("human", "false");
|
||||
}
|
||||
if (false == infoRequest.getCategories().equals(EnumSet.allOf(XPackInfoRequest.Category.class))) {
|
||||
request.addParameter("categories", infoRequest.getCategories().stream()
|
||||
.map(c -> c.toString().toLowerCase(Locale.ROOT))
|
||||
.collect(Collectors.joining(",")));
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
|
||||
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
|
||||
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));
|
||||
|
@ -1246,7 +1277,7 @@ final class RequestConverters {
|
|||
}
|
||||
|
||||
Params withIndicesOptions(IndicesOptions indicesOptions) {
|
||||
putParam("ignore_unavailable", Boolean.toString(indicesOptions.ignoreUnavailable()));
|
||||
withIgnoreUnavailable(indicesOptions.ignoreUnavailable());
|
||||
putParam("allow_no_indices", Boolean.toString(indicesOptions.allowNoIndices()));
|
||||
String expandWildcards;
|
||||
if (indicesOptions.expandWildcardsOpen() == false && indicesOptions.expandWildcardsClosed() == false) {
|
||||
|
@ -1265,6 +1296,12 @@ final class RequestConverters {
|
|||
return this;
|
||||
}
|
||||
|
||||
Params withIgnoreUnavailable(boolean ignoreUnavailable) {
|
||||
// Always explicitly place the ignore_unavailable value.
|
||||
putParam("ignore_unavailable", Boolean.toString(ignoreUnavailable));
|
||||
return this;
|
||||
}
|
||||
|
||||
Params withHuman(boolean human) {
|
||||
if (human) {
|
||||
putParam("human", Boolean.toString(human));
|
||||
|
|
|
@ -202,6 +202,7 @@ public class RestHighLevelClient implements Closeable {
|
|||
private final IngestClient ingestClient = new IngestClient(this);
|
||||
private final SnapshotClient snapshotClient = new SnapshotClient(this);
|
||||
private final TasksClient tasksClient = new TasksClient(this);
|
||||
private final XPackClient xPackClient = new XPackClient(this);
|
||||
|
||||
/**
|
||||
* Creates a {@link RestHighLevelClient} given the low level {@link RestClientBuilder} that allows to build the
|
||||
|
@ -292,6 +293,19 @@ public class RestHighLevelClient implements Closeable {
|
|||
return tasksClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for the {@link RestHighLevelClient} that provides methods for
|
||||
* accessing the Elastic Licensed X-Pack APIs that are shipped with the
|
||||
* default distribution of Elasticsearch. All of these APIs will 404 if run
|
||||
* against the OSS distribution of Elasticsearch.
|
||||
* <p>
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/xpack-api.html">
|
||||
* X-Pack APIs on elastic.co</a> for more information.
|
||||
*/
|
||||
public final XPackClient xpack() {
|
||||
return xPackClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a bulk request using the Bulk API.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html">Bulk API on elastic.co</a>
|
||||
|
@ -668,7 +682,7 @@ public class RestHighLevelClient implements Closeable {
|
|||
emptySet());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Executes a request using the Multi Search Template API.
|
||||
*
|
||||
|
@ -678,9 +692,9 @@ public class RestHighLevelClient implements Closeable {
|
|||
public final MultiSearchTemplateResponse multiSearchTemplate(MultiSearchTemplateRequest multiSearchTemplateRequest,
|
||||
RequestOptions options) throws IOException {
|
||||
return performRequestAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
|
||||
options, MultiSearchTemplateResponse::fromXContext, emptySet());
|
||||
}
|
||||
|
||||
options, MultiSearchTemplateResponse::fromXContext, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously executes a request using the Multi Search Template API
|
||||
*
|
||||
|
@ -692,7 +706,7 @@ public class RestHighLevelClient implements Closeable {
|
|||
ActionListener<MultiSearchTemplateResponse> listener) {
|
||||
performRequestAsyncAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
|
||||
options, MultiSearchTemplateResponse::fromXContext, listener, emptySet());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously executes a request using the Ranking Evaluation API.
|
||||
|
|
|
@ -30,6 +30,8 @@ import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyReposito
|
|||
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotResponse;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
|
||||
|
@ -221,6 +223,35 @@ public final class SnapshotClient {
|
|||
GetSnapshotsResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of requested snapshots.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
|
||||
* API on elastic.co</a>
|
||||
* @param snapshotsStatusRequest the request
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @return the response
|
||||
* @throws IOException in case there is a problem sending the request or parsing back the response
|
||||
*/
|
||||
public SnapshotsStatusResponse status(SnapshotsStatusRequest snapshotsStatusRequest, RequestOptions options)
|
||||
throws IOException {
|
||||
return restHighLevelClient.performRequestAndParseEntity(snapshotsStatusRequest, RequestConverters::snapshotsStatus, options,
|
||||
SnapshotsStatusResponse::fromXContent, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously gets the status of requested snapshots.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
|
||||
* API on elastic.co</a>
|
||||
* @param snapshotsStatusRequest the request
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @param listener the listener to be notified upon request completion
|
||||
*/
|
||||
public void statusAsync(SnapshotsStatusRequest snapshotsStatusRequest, RequestOptions options,
|
||||
ActionListener<SnapshotsStatusResponse> listener) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(snapshotsStatusRequest, RequestConverters::snapshotsStatus, options,
|
||||
SnapshotsStatusResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a snapshot.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
/**
|
||||
* A wrapper for the {@link RestHighLevelClient} that provides methods for
|
||||
* accessing the Elastic Licensed X-Pack APIs that are shipped with the
|
||||
* default distribution of Elasticsearch. All of these APIs will 404 if run
|
||||
* against the OSS distribution of Elasticsearch.
|
||||
* <p>
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/xpack-api.html">
|
||||
* X-Pack APIs on elastic.co</a> for more information.
|
||||
*/
|
||||
public final class XPackClient {
|
||||
private final RestHighLevelClient restHighLevelClient;
|
||||
|
||||
XPackClient(RestHighLevelClient restHighLevelClient) {
|
||||
this.restHighLevelClient = restHighLevelClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch information about X-Pack from the cluster.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/info-api.html">
|
||||
* the docs</a> for more.
|
||||
* @param request the request
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @return the response
|
||||
* @throws IOException in case there is a problem sending the request or parsing back the response
|
||||
*/
|
||||
public XPackInfoResponse info(XPackInfoRequest request, RequestOptions options) throws IOException {
|
||||
return restHighLevelClient.performRequestAndParseEntity(request, RequestConverters::xPackInfo, options,
|
||||
XPackInfoResponse::fromXContent, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously fetch information about X-Pack from the cluster.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/info-api.html">
|
||||
* the docs</a> for more.
|
||||
* @param request the request
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @param listener the listener to be notified upon request completion
|
||||
*/
|
||||
public void infoAsync(XPackInfoRequest request, RequestOptions options,
|
||||
ActionListener<XPackInfoResponse> listener) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::xPackInfo, options,
|
||||
XPackInfoResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
}
|
|
@ -612,7 +612,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
createIndex(index, Settings.EMPTY);
|
||||
closeIndex(index);
|
||||
ResponseException exception = expectThrows(ResponseException.class,
|
||||
() -> client().performRequest(HttpGet.METHOD_NAME, index + "/_search"));
|
||||
() -> client().performRequest(new Request(HttpGet.METHOD_NAME, index + "/_search")));
|
||||
assertThat(exception.getResponse().getStatusLine().getStatusCode(), equalTo(RestStatus.BAD_REQUEST.getStatus()));
|
||||
assertThat(exception.getMessage().contains(index), equalTo(true));
|
||||
|
||||
|
@ -621,7 +621,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
highLevelClient().indices()::openAsync);
|
||||
assertTrue(openIndexResponse.isAcknowledged());
|
||||
|
||||
Response response = client().performRequest(HttpGet.METHOD_NAME, index + "/_search");
|
||||
Response response = client().performRequest(new Request(HttpGet.METHOD_NAME, index + "/_search"));
|
||||
assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus()));
|
||||
}
|
||||
|
||||
|
@ -650,7 +650,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
public void testCloseExistingIndex() throws IOException {
|
||||
String index = "index";
|
||||
createIndex(index, Settings.EMPTY);
|
||||
Response response = client().performRequest(HttpGet.METHOD_NAME, index + "/_search");
|
||||
Response response = client().performRequest(new Request(HttpGet.METHOD_NAME, index + "/_search"));
|
||||
assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus()));
|
||||
|
||||
CloseIndexRequest closeIndexRequest = new CloseIndexRequest(index);
|
||||
|
@ -659,7 +659,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
assertTrue(closeIndexResponse.isAcknowledged());
|
||||
|
||||
ResponseException exception = expectThrows(ResponseException.class,
|
||||
() -> client().performRequest(HttpGet.METHOD_NAME, index + "/_search"));
|
||||
() -> client().performRequest(new Request(HttpGet.METHOD_NAME, index + "/_search")));
|
||||
assertThat(exception.getResponse().getStatusLine().getStatusCode(), equalTo(RestStatus.BAD_REQUEST.getStatus()));
|
||||
assertThat(exception.getMessage().contains(index), equalTo(true));
|
||||
}
|
||||
|
@ -817,7 +817,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
assertFalse(execute(getAliasesRequest, highLevelClient().indices()::existsAlias, highLevelClient().indices()::existsAliasAsync));
|
||||
|
||||
createIndex("index", Settings.EMPTY);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index/_alias/alias");
|
||||
client().performRequest(new Request(HttpPut.METHOD_NAME, "/index/_alias/alias"));
|
||||
assertTrue(execute(getAliasesRequest, highLevelClient().indices()::existsAlias, highLevelClient().indices()::existsAliasAsync));
|
||||
|
||||
GetAliasesRequest getAliasesRequest2 = new GetAliasesRequest();
|
||||
|
@ -936,10 +936,10 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
public void testGetAlias() throws IOException {
|
||||
{
|
||||
createIndex("index1", Settings.EMPTY);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index1/_alias/alias1");
|
||||
client().performRequest(new Request(HttpPut.METHOD_NAME, "/index1/_alias/alias1"));
|
||||
|
||||
createIndex("index2", Settings.EMPTY);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index2/_alias/alias2");
|
||||
client().performRequest(new Request(HttpPut.METHOD_NAME, "/index2/_alias/alias2"));
|
||||
|
||||
createIndex("index3", Settings.EMPTY);
|
||||
}
|
||||
|
@ -1075,7 +1075,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
assertThat(getAliasesResponse.getError(), equalTo("alias [" + alias + "] missing"));
|
||||
}
|
||||
createIndex(index, Settings.EMPTY);
|
||||
client().performRequest(HttpPut.METHOD_NAME, index + "/_alias/" + alias);
|
||||
client().performRequest(new Request(HttpPut.METHOD_NAME, index + "/_alias/" + alias));
|
||||
{
|
||||
GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index, "non_existent_index");
|
||||
GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias,
|
||||
|
|
|
@ -21,8 +21,13 @@ package org.elasticsearch.client;
|
|||
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.elasticsearch.action.main.MainResponse;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
|
||||
import org.elasticsearch.protocol.xpack.license.LicenseStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
|
||||
public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
|
||||
|
@ -31,16 +36,16 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
|
|||
assertTrue(highLevelClient().ping(RequestOptions.DEFAULT));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testInfo() throws IOException {
|
||||
MainResponse info = highLevelClient().info(RequestOptions.DEFAULT);
|
||||
// compare with what the low level client outputs
|
||||
Map<String, Object> infoAsMap = entityAsMap(adminClient().performRequest(HttpGet.METHOD_NAME, "/"));
|
||||
Map<String, Object> infoAsMap = entityAsMap(adminClient().performRequest(new Request(HttpGet.METHOD_NAME, "/")));
|
||||
assertEquals(infoAsMap.get("cluster_name"), info.getClusterName().value());
|
||||
assertEquals(infoAsMap.get("cluster_uuid"), info.getClusterUuid());
|
||||
|
||||
// only check node name existence, might be a different one from what was hit by low level client in multi-node cluster
|
||||
assertNotNull(info.getNodeName());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> versionMap = (Map<String, Object>) infoAsMap.get("version");
|
||||
assertEquals(versionMap.get("build_flavor"), info.getBuild().flavor().displayName());
|
||||
assertEquals(versionMap.get("build_type"), info.getBuild().type().displayName());
|
||||
|
@ -51,4 +56,49 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
|
|||
assertEquals(versionMap.get("lucene_version"), info.getVersion().luceneVersion.toString());
|
||||
}
|
||||
|
||||
public void testXPackInfo() throws IOException {
|
||||
XPackInfoRequest request = new XPackInfoRequest();
|
||||
request.setCategories(EnumSet.allOf(XPackInfoRequest.Category.class));
|
||||
request.setVerbose(true);
|
||||
XPackInfoResponse info = highLevelClient().xpack().info(request, RequestOptions.DEFAULT);
|
||||
|
||||
MainResponse mainResponse = highLevelClient().info(RequestOptions.DEFAULT);
|
||||
|
||||
assertEquals(mainResponse.getBuild().shortHash(), info.getBuildInfo().getHash());
|
||||
|
||||
assertEquals("basic", info.getLicenseInfo().getType());
|
||||
assertEquals("basic", info.getLicenseInfo().getMode());
|
||||
assertEquals(LicenseStatus.ACTIVE, info.getLicenseInfo().getStatus());
|
||||
|
||||
FeatureSet graph = info.getFeatureSetsInfo().getFeatureSets().get("graph");
|
||||
assertNotNull(graph.description());
|
||||
assertFalse(graph.available());
|
||||
assertTrue(graph.enabled());
|
||||
assertNull(graph.nativeCodeInfo());
|
||||
FeatureSet monitoring = info.getFeatureSetsInfo().getFeatureSets().get("monitoring");
|
||||
assertNotNull(monitoring.description());
|
||||
assertTrue(monitoring.available());
|
||||
assertTrue(monitoring.enabled());
|
||||
assertNull(monitoring.nativeCodeInfo());
|
||||
FeatureSet ml = info.getFeatureSetsInfo().getFeatureSets().get("ml");
|
||||
assertNotNull(ml.description());
|
||||
assertFalse(ml.available());
|
||||
assertTrue(ml.enabled());
|
||||
assertEquals(mainResponse.getVersion().toString(),
|
||||
ml.nativeCodeInfo().get("version").toString().replace("-SNAPSHOT", ""));
|
||||
}
|
||||
|
||||
public void testXPackInfoEmptyRequest() throws IOException {
|
||||
XPackInfoResponse info = highLevelClient().xpack().info(new XPackInfoRequest(), RequestOptions.DEFAULT);
|
||||
|
||||
/*
|
||||
* The default in the transport client is non-verbose and returning
|
||||
* no categories which is the opposite of the default when you use
|
||||
* the API over REST. We don't want to break the transport client
|
||||
* even though it doesn't feel like a good default.
|
||||
*/
|
||||
assertNull(info.getBuildInfo());
|
||||
assertNull(info.getLicenseInfo());
|
||||
assertNull(info.getFeatureSetsInfo());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
package org.elasticsearch.client;
|
||||
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
||||
|
@ -37,7 +35,6 @@ import org.junit.Before;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -49,19 +46,17 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
|
|||
|
||||
@Before
|
||||
public void indexDocuments() throws IOException {
|
||||
StringEntity doc = new StringEntity("{\"text\":\"berlin\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest("PUT", "/index/doc/1", Collections.emptyMap(), doc);
|
||||
doc = new StringEntity("{\"text\":\"amsterdam\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest("PUT", "/index/doc/2", Collections.emptyMap(), doc);
|
||||
client().performRequest("PUT", "/index/doc/3", Collections.emptyMap(), doc);
|
||||
client().performRequest("PUT", "/index/doc/4", Collections.emptyMap(), doc);
|
||||
client().performRequest("PUT", "/index/doc/5", Collections.emptyMap(), doc);
|
||||
client().performRequest("PUT", "/index/doc/6", Collections.emptyMap(), doc);
|
||||
client().performRequest("POST", "/index/_refresh");
|
||||
|
||||
// add another index to test basic multi index support
|
||||
client().performRequest("PUT", "/index2/doc/7", Collections.emptyMap(), doc);
|
||||
client().performRequest("POST", "/index2/_refresh");
|
||||
Request berlin = new Request("PUT", "/index/doc/berlin");
|
||||
berlin.setJsonEntity("{\"text\":\"berlin\"}");
|
||||
client().performRequest(berlin);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
// add another index to test basic multi index support
|
||||
String index = i == 0 ? "index2" : "index";
|
||||
Request amsterdam = new Request("PUT", "/" + index + "/doc/amsterdam" + i);
|
||||
amsterdam.setJsonEntity("{\"text\":\"amsterdam\"}");
|
||||
client().performRequest(amsterdam);
|
||||
}
|
||||
client().performRequest(new Request("POST", "/_refresh"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,10 +66,10 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
|
|||
public void testRankEvalRequest() throws IOException {
|
||||
SearchSourceBuilder testQuery = new SearchSourceBuilder();
|
||||
testQuery.query(new MatchAllQueryBuilder());
|
||||
List<RatedDocument> amsterdamRatedDocs = createRelevant("index" , "2", "3", "4", "5");
|
||||
amsterdamRatedDocs.addAll(createRelevant("index2", "7"));
|
||||
List<RatedDocument> amsterdamRatedDocs = createRelevant("index" , "amsterdam1", "amsterdam2", "amsterdam3", "amsterdam4");
|
||||
amsterdamRatedDocs.addAll(createRelevant("index2", "amsterdam0"));
|
||||
RatedRequest amsterdamRequest = new RatedRequest("amsterdam_query", amsterdamRatedDocs, testQuery);
|
||||
RatedRequest berlinRequest = new RatedRequest("berlin_query", createRelevant("index", "1"), testQuery);
|
||||
RatedRequest berlinRequest = new RatedRequest("berlin_query", createRelevant("index", "berlin"), testQuery);
|
||||
List<RatedRequest> specifications = new ArrayList<>();
|
||||
specifications.add(amsterdamRequest);
|
||||
specifications.add(berlinRequest);
|
||||
|
@ -94,7 +89,7 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
|
|||
assertEquals(7, hitsAndRatings.size());
|
||||
for (RatedSearchHit hit : hitsAndRatings) {
|
||||
String id = hit.getSearchHit().getId();
|
||||
if (id.equals("1") || id.equals("6")) {
|
||||
if (id.equals("berlin") || id.equals("amsterdam5")) {
|
||||
assertFalse(hit.getRating().isPresent());
|
||||
} else {
|
||||
assertEquals(1, hit.getRating().get().intValue());
|
||||
|
@ -106,7 +101,7 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
|
|||
assertEquals(7, hitsAndRatings.size());
|
||||
for (RatedSearchHit hit : hitsAndRatings) {
|
||||
String id = hit.getSearchHit().getId();
|
||||
if (id.equals("1")) {
|
||||
if (id.equals("berlin")) {
|
||||
assertEquals(1, hit.getRating().get().intValue());
|
||||
} else {
|
||||
assertFalse(hit.getRating().isPresent());
|
||||
|
@ -114,7 +109,7 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
|
|||
}
|
||||
|
||||
// now try this when test2 is closed
|
||||
client().performRequest("POST", "index2/_close", Collections.emptyMap());
|
||||
client().performRequest(new Request("POST", "index2/_close"));
|
||||
rankEvalRequest.indicesOptions(IndicesOptions.fromParameters(null, "true", null, SearchRequest.DEFAULT_INDICES_OPTIONS));
|
||||
response = execute(rankEvalRequest, highLevelClient()::rankEval, highLevelClient()::rankEvalAsync);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotReq
|
|||
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
|
||||
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
|
||||
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
|
@ -123,6 +124,7 @@ import org.elasticsearch.index.rankeval.RankEvalRequest;
|
|||
import org.elasticsearch.index.rankeval.RankEvalSpec;
|
||||
import org.elasticsearch.index.rankeval.RatedRequest;
|
||||
import org.elasticsearch.index.rankeval.RestRankEvalAction;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
|
||||
import org.elasticsearch.repositories.fs.FsRepository;
|
||||
import org.elasticsearch.rest.action.search.RestSearchAction;
|
||||
import org.elasticsearch.script.ScriptType;
|
||||
|
@ -150,6 +152,7 @@ import java.nio.file.Path;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -173,6 +176,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXC
|
|||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.hasKey;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
|
@ -2169,6 +2173,29 @@ public class RequestConvertersTests extends ESTestCase {
|
|||
assertNull(request.getEntity());
|
||||
}
|
||||
|
||||
public void testSnapshotsStatus() {
|
||||
Map<String, String> expectedParams = new HashMap<>();
|
||||
String repository = randomIndicesNames(1, 1)[0];
|
||||
String[] snapshots = randomIndicesNames(1, 5);
|
||||
StringBuilder snapshotNames = new StringBuilder(snapshots[0]);
|
||||
for (int idx = 1; idx < snapshots.length; idx++) {
|
||||
snapshotNames.append(",").append(snapshots[idx]);
|
||||
}
|
||||
boolean ignoreUnavailable = randomBoolean();
|
||||
String endpoint = "/_snapshot/" + repository + "/" + snapshotNames.toString() + "/_status";
|
||||
|
||||
SnapshotsStatusRequest snapshotsStatusRequest = new SnapshotsStatusRequest(repository, snapshots);
|
||||
setRandomMasterTimeout(snapshotsStatusRequest, expectedParams);
|
||||
snapshotsStatusRequest.ignoreUnavailable(ignoreUnavailable);
|
||||
expectedParams.put("ignore_unavailable", Boolean.toString(ignoreUnavailable));
|
||||
|
||||
Request request = RequestConverters.snapshotsStatus(snapshotsStatusRequest);
|
||||
assertThat(request.getEndpoint(), equalTo(endpoint));
|
||||
assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME));
|
||||
assertThat(request.getParameters(), equalTo(expectedParams));
|
||||
assertThat(request.getEntity(), is(nullValue()));
|
||||
}
|
||||
|
||||
public void testDeleteSnapshot() {
|
||||
Map<String, String> expectedParams = new HashMap<>();
|
||||
String repository = randomIndicesNames(1, 1)[0];
|
||||
|
@ -2465,6 +2492,37 @@ public class RequestConvertersTests extends ESTestCase {
|
|||
+ "previous requests have content-type [" + xContentType + "]", exception.getMessage());
|
||||
}
|
||||
|
||||
public void testXPackInfo() {
|
||||
XPackInfoRequest infoRequest = new XPackInfoRequest();
|
||||
Map<String, String> expectedParams = new HashMap<>();
|
||||
infoRequest.setVerbose(randomBoolean());
|
||||
if (false == infoRequest.isVerbose()) {
|
||||
expectedParams.put("human", "false");
|
||||
}
|
||||
int option = between(0, 2);
|
||||
switch (option) {
|
||||
case 0:
|
||||
infoRequest.setCategories(EnumSet.allOf(XPackInfoRequest.Category.class));
|
||||
break;
|
||||
case 1:
|
||||
infoRequest.setCategories(EnumSet.of(XPackInfoRequest.Category.FEATURES));
|
||||
expectedParams.put("categories", "features");
|
||||
break;
|
||||
case 2:
|
||||
infoRequest.setCategories(EnumSet.of(XPackInfoRequest.Category.FEATURES, XPackInfoRequest.Category.BUILD));
|
||||
expectedParams.put("categories", "build,features");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("invalid option [" + option + "]");
|
||||
}
|
||||
|
||||
Request request = RequestConverters.xPackInfo(infoRequest);
|
||||
assertEquals(HttpGet.METHOD_NAME, request.getMethod());
|
||||
assertEquals("/_xpack", request.getEndpoint());
|
||||
assertNull(request.getEntity());
|
||||
assertEquals(expectedParams, request.getParameters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomize the {@link FetchSourceContext} request parameters.
|
||||
*/
|
||||
|
|
|
@ -19,12 +19,8 @@
|
|||
|
||||
package org.elasticsearch.client;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.nio.entity.NStringEntity;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.action.explain.ExplainRequest;
|
||||
|
@ -101,85 +97,106 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
|
||||
@Before
|
||||
public void indexDocuments() throws IOException {
|
||||
StringEntity doc1 = new StringEntity("{\"type\":\"type1\", \"num\":10, \"num2\":50}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index/type/1", Collections.emptyMap(), doc1);
|
||||
StringEntity doc2 = new StringEntity("{\"type\":\"type1\", \"num\":20, \"num2\":40}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index/type/2", Collections.emptyMap(), doc2);
|
||||
StringEntity doc3 = new StringEntity("{\"type\":\"type1\", \"num\":50, \"num2\":35}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index/type/3", Collections.emptyMap(), doc3);
|
||||
StringEntity doc4 = new StringEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index/type/4", Collections.emptyMap(), doc4);
|
||||
StringEntity doc5 = new StringEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index/type/5", Collections.emptyMap(), doc5);
|
||||
client().performRequest(HttpPost.METHOD_NAME, "/index/_refresh");
|
||||
{
|
||||
Request doc1 = new Request(HttpPut.METHOD_NAME, "/index/type/1");
|
||||
doc1.setJsonEntity("{\"type\":\"type1\", \"num\":10, \"num2\":50}");
|
||||
client().performRequest(doc1);
|
||||
Request doc2 = new Request(HttpPut.METHOD_NAME, "/index/type/2");
|
||||
doc2.setJsonEntity("{\"type\":\"type1\", \"num\":20, \"num2\":40}");
|
||||
client().performRequest(doc2);
|
||||
Request doc3 = new Request(HttpPut.METHOD_NAME, "/index/type/3");
|
||||
doc3.setJsonEntity("{\"type\":\"type1\", \"num\":50, \"num2\":35}");
|
||||
client().performRequest(doc3);
|
||||
Request doc4 = new Request(HttpPut.METHOD_NAME, "/index/type/4");
|
||||
doc4.setJsonEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}");
|
||||
client().performRequest(doc4);
|
||||
Request doc5 = new Request(HttpPut.METHOD_NAME, "/index/type/5");
|
||||
doc5.setJsonEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}");
|
||||
client().performRequest(doc5);
|
||||
}
|
||||
|
||||
{
|
||||
Request doc1 = new Request(HttpPut.METHOD_NAME, "/index1/doc/1");
|
||||
doc1.setJsonEntity("{\"field\":\"value1\", \"rating\": 7}");
|
||||
client().performRequest(doc1);
|
||||
Request doc2 = new Request(HttpPut.METHOD_NAME, "/index1/doc/2");
|
||||
doc2.setJsonEntity("{\"field\":\"value2\"}");
|
||||
client().performRequest(doc2);
|
||||
}
|
||||
|
||||
StringEntity doc = new StringEntity("{\"field\":\"value1\", \"rating\": 7}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index1/doc/1", Collections.emptyMap(), doc);
|
||||
doc = new StringEntity("{\"field\":\"value2\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index1/doc/2", Collections.emptyMap(), doc);
|
||||
|
||||
StringEntity mappings = new StringEntity(
|
||||
"{" +
|
||||
" \"mappings\": {" +
|
||||
" \"doc\": {" +
|
||||
" \"properties\": {" +
|
||||
" \"rating\": {" +
|
||||
" \"type\": \"keyword\"" +
|
||||
" }" +
|
||||
" }" +
|
||||
" }" +
|
||||
" }" +
|
||||
"}}",
|
||||
ContentType.APPLICATION_JSON);
|
||||
client().performRequest("PUT", "/index2", Collections.emptyMap(), mappings);
|
||||
doc = new StringEntity("{\"field\":\"value1\", \"rating\": \"good\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index2/doc/3", Collections.emptyMap(), doc);
|
||||
doc = new StringEntity("{\"field\":\"value2\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index2/doc/4", Collections.emptyMap(), doc);
|
||||
|
||||
doc = new StringEntity("{\"field\":\"value1\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index3/doc/5", Collections.emptyMap(), doc);
|
||||
doc = new StringEntity("{\"field\":\"value2\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index3/doc/6", Collections.emptyMap(), doc);
|
||||
|
||||
mappings = new StringEntity(
|
||||
"{" +
|
||||
{
|
||||
Request create = new Request("PUT", "/index2");
|
||||
create.setJsonEntity(
|
||||
"{" +
|
||||
" \"mappings\": {" +
|
||||
" \"doc\": {" +
|
||||
" \"properties\": {" +
|
||||
" \"field1\": {" +
|
||||
" \"type\": \"keyword\"," +
|
||||
" \"store\": true" +
|
||||
" }," +
|
||||
" \"field2\": {" +
|
||||
" \"type\": \"keyword\"," +
|
||||
" \"store\": true" +
|
||||
" \"rating\": {" +
|
||||
" \"type\": \"keyword\"" +
|
||||
" }" +
|
||||
" }" +
|
||||
" }" +
|
||||
" }" +
|
||||
"}}",
|
||||
ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index4", Collections.emptyMap(), mappings);
|
||||
doc = new StringEntity("{\"field1\":\"value1\", \"field2\":\"value2\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/index4/doc/1", Collections.emptyMap(), doc);
|
||||
StringEntity aliasFilter = new StringEntity(
|
||||
"{" +
|
||||
" \"actions\" : [" +
|
||||
" {" +
|
||||
" \"add\" : {" +
|
||||
" \"index\" : \"index4\"," +
|
||||
" \"alias\" : \"alias4\"," +
|
||||
" \"filter\" : { \"term\" : { \"field2\" : \"value1\" } }" +
|
||||
" }" +
|
||||
" }" +
|
||||
" ]" +
|
||||
"}",
|
||||
ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPost.METHOD_NAME, "/_aliases", Collections.emptyMap(), aliasFilter);
|
||||
"}");
|
||||
client().performRequest(create);
|
||||
Request doc3 = new Request(HttpPut.METHOD_NAME, "/index2/doc/3");
|
||||
doc3.setJsonEntity("{\"field\":\"value1\", \"rating\": \"good\"}");
|
||||
client().performRequest(doc3);
|
||||
Request doc4 = new Request(HttpPut.METHOD_NAME, "/index2/doc/4");
|
||||
doc4.setJsonEntity("{\"field\":\"value2\"}");
|
||||
client().performRequest(doc4);
|
||||
}
|
||||
|
||||
client().performRequest(HttpPost.METHOD_NAME, "/index1,index2,index3,index4/_refresh");
|
||||
{
|
||||
Request doc5 = new Request(HttpPut.METHOD_NAME, "/index3/doc/5");
|
||||
doc5.setJsonEntity("{\"field\":\"value1\"}");
|
||||
client().performRequest(doc5);
|
||||
Request doc6 = new Request(HttpPut.METHOD_NAME, "/index3/doc/6");
|
||||
doc6.setJsonEntity("{\"field\":\"value2\"}");
|
||||
client().performRequest(doc6);
|
||||
}
|
||||
|
||||
{
|
||||
Request create = new Request(HttpPut.METHOD_NAME, "/index4");
|
||||
create.setJsonEntity(
|
||||
"{" +
|
||||
" \"mappings\": {" +
|
||||
" \"doc\": {" +
|
||||
" \"properties\": {" +
|
||||
" \"field1\": {" +
|
||||
" \"type\": \"keyword\"," +
|
||||
" \"store\": true" +
|
||||
" }," +
|
||||
" \"field2\": {" +
|
||||
" \"type\": \"keyword\"," +
|
||||
" \"store\": true" +
|
||||
" }" +
|
||||
" }" +
|
||||
" }" +
|
||||
" }" +
|
||||
"}");
|
||||
client().performRequest(create);
|
||||
Request doc1 = new Request(HttpPut.METHOD_NAME, "/index4/doc/1");
|
||||
doc1.setJsonEntity("{\"field1\":\"value1\", \"field2\":\"value2\"}");
|
||||
client().performRequest(doc1);
|
||||
|
||||
Request createFilteredAlias = new Request(HttpPost.METHOD_NAME, "/_aliases");
|
||||
createFilteredAlias.setJsonEntity(
|
||||
"{" +
|
||||
" \"actions\" : [" +
|
||||
" {" +
|
||||
" \"add\" : {" +
|
||||
" \"index\" : \"index4\"," +
|
||||
" \"alias\" : \"alias4\"," +
|
||||
" \"filter\" : { \"term\" : { \"field2\" : \"value1\" } }" +
|
||||
" }" +
|
||||
" }" +
|
||||
" ]" +
|
||||
"}");
|
||||
client().performRequest(createFilteredAlias);
|
||||
}
|
||||
|
||||
client().performRequest(new Request(HttpPost.METHOD_NAME, "/_refresh"));
|
||||
}
|
||||
|
||||
public void testSearchNoQuery() throws IOException {
|
||||
|
@ -377,7 +394,9 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
|
||||
public void testSearchWithParentJoin() throws IOException {
|
||||
final String indexName = "child_example";
|
||||
StringEntity parentMapping = new StringEntity("{\n" +
|
||||
Request createIndex = new Request(HttpPut.METHOD_NAME, "/" + indexName);
|
||||
createIndex.setJsonEntity(
|
||||
"{\n" +
|
||||
" \"mappings\": {\n" +
|
||||
" \"qa\" : {\n" +
|
||||
" \"properties\" : {\n" +
|
||||
|
@ -388,9 +407,11 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
" }\n" +
|
||||
" }\n" +
|
||||
" }" +
|
||||
"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/" + indexName, Collections.emptyMap(), parentMapping);
|
||||
StringEntity questionDoc = new StringEntity("{\n" +
|
||||
"}");
|
||||
client().performRequest(createIndex);
|
||||
Request questionDoc = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/qa/1");
|
||||
questionDoc.setJsonEntity(
|
||||
"{\n" +
|
||||
" \"body\": \"<p>I have Windows 2003 server and i bought a new Windows 2008 server...\",\n" +
|
||||
" \"title\": \"Whats the best way to file transfer my site from server to a newer one?\",\n" +
|
||||
" \"tags\": [\n" +
|
||||
|
@ -399,9 +420,12 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
" \"file-transfer\"\n" +
|
||||
" ],\n" +
|
||||
" \"qa_join_field\" : \"question\"\n" +
|
||||
"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/" + indexName + "/qa/1", Collections.emptyMap(), questionDoc);
|
||||
StringEntity answerDoc1 = new StringEntity("{\n" +
|
||||
"}");
|
||||
client().performRequest(questionDoc);
|
||||
Request answerDoc1 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/qa/2");
|
||||
answerDoc1.addParameter("routing", "1");
|
||||
answerDoc1.setJsonEntity(
|
||||
"{\n" +
|
||||
" \"owner\": {\n" +
|
||||
" \"location\": \"Norfolk, United Kingdom\",\n" +
|
||||
" \"display_name\": \"Sam\",\n" +
|
||||
|
@ -413,9 +437,12 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
" \"parent\" : \"1\"\n" +
|
||||
" },\n" +
|
||||
" \"creation_date\": \"2009-05-04T13:45:37.030\"\n" +
|
||||
"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/" + indexName + "/qa/2", Collections.singletonMap("routing", "1"), answerDoc1);
|
||||
StringEntity answerDoc2 = new StringEntity("{\n" +
|
||||
"}");
|
||||
client().performRequest(answerDoc1);
|
||||
Request answerDoc2 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/qa/3");
|
||||
answerDoc2.addParameter("routing", "1");
|
||||
answerDoc2.setJsonEntity(
|
||||
"{\n" +
|
||||
" \"owner\": {\n" +
|
||||
" \"location\": \"Norfolk, United Kingdom\",\n" +
|
||||
" \"display_name\": \"Troll\",\n" +
|
||||
|
@ -427,9 +454,9 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
" \"parent\" : \"1\"\n" +
|
||||
" },\n" +
|
||||
" \"creation_date\": \"2009-05-05T13:45:37.030\"\n" +
|
||||
"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "/" + indexName + "/qa/3", Collections.singletonMap("routing", "1"), answerDoc2);
|
||||
client().performRequest(HttpPost.METHOD_NAME, "/_refresh");
|
||||
"}");
|
||||
client().performRequest(answerDoc2);
|
||||
client().performRequest(new Request(HttpPost.METHOD_NAME, "/_refresh"));
|
||||
|
||||
TermsAggregationBuilder leafTermAgg = new TermsAggregationBuilder("top-names", ValueType.STRING)
|
||||
.field("owner.display_name.keyword").size(10);
|
||||
|
@ -506,9 +533,10 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
}
|
||||
|
||||
public void testSearchWithWeirdScriptFields() throws Exception {
|
||||
HttpEntity entity = new NStringEntity("{ \"field\":\"value\"}", ContentType.APPLICATION_JSON);
|
||||
client().performRequest("PUT", "test/type/1", Collections.emptyMap(), entity);
|
||||
client().performRequest("POST", "/test/_refresh");
|
||||
Request doc = new Request("PUT", "test/type/1");
|
||||
doc.setJsonEntity("{\"field\":\"value\"}");
|
||||
client().performRequest(doc);
|
||||
client().performRequest(new Request("POST", "/test/_refresh"));
|
||||
|
||||
{
|
||||
SearchRequest searchRequest = new SearchRequest("test").source(SearchSourceBuilder.searchSource()
|
||||
|
@ -547,13 +575,13 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
}
|
||||
|
||||
public void testSearchScroll() throws Exception {
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
XContentBuilder builder = jsonBuilder().startObject().field("field", i).endObject();
|
||||
HttpEntity entity = new NStringEntity(Strings.toString(builder), ContentType.APPLICATION_JSON);
|
||||
client().performRequest(HttpPut.METHOD_NAME, "test/type1/" + Integer.toString(i), Collections.emptyMap(), entity);
|
||||
Request doc = new Request(HttpPut.METHOD_NAME, "/test/type1/" + Integer.toString(i));
|
||||
doc.setJsonEntity(Strings.toString(builder));
|
||||
client().performRequest(doc);
|
||||
}
|
||||
client().performRequest(HttpPost.METHOD_NAME, "/test/_refresh");
|
||||
client().performRequest(new Request(HttpPost.METHOD_NAME, "/test/_refresh"));
|
||||
|
||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().size(35).sort("field", SortOrder.ASC);
|
||||
SearchRequest searchRequest = new SearchRequest("test").scroll(TimeValue.timeValueMinutes(2)).source(searchSourceBuilder);
|
||||
|
@ -878,11 +906,11 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
|
||||
assertToXContentEquivalent(expectedSource, actualSource, XContentType.JSON);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void testMultiSearchTemplate() throws Exception {
|
||||
MultiSearchTemplateRequest multiSearchTemplateRequest = new MultiSearchTemplateRequest();
|
||||
|
||||
|
||||
SearchTemplateRequest goodRequest = new SearchTemplateRequest();
|
||||
goodRequest.setRequest(new SearchRequest("index"));
|
||||
goodRequest.setScriptType(ScriptType.INLINE);
|
||||
|
@ -900,8 +928,8 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
goodRequest.setExplain(true);
|
||||
goodRequest.setProfile(true);
|
||||
multiSearchTemplateRequest.add(goodRequest);
|
||||
|
||||
|
||||
|
||||
|
||||
SearchTemplateRequest badRequest = new SearchTemplateRequest();
|
||||
badRequest.setRequest(new SearchRequest("index"));
|
||||
badRequest.setScriptType(ScriptType.INLINE);
|
||||
|
@ -910,17 +938,17 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
scriptParams.put("number", 10);
|
||||
badRequest.setScriptParams(scriptParams);
|
||||
|
||||
multiSearchTemplateRequest.add(badRequest);
|
||||
|
||||
multiSearchTemplateRequest.add(badRequest);
|
||||
|
||||
MultiSearchTemplateResponse multiSearchTemplateResponse =
|
||||
execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
|
||||
execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
|
||||
highLevelClient()::multiSearchTemplateAsync);
|
||||
|
||||
|
||||
Item[] responses = multiSearchTemplateResponse.getResponses();
|
||||
|
||||
|
||||
assertEquals(2, responses.length);
|
||||
|
||||
|
||||
|
||||
|
||||
assertNull(responses[0].getResponse().getSource());
|
||||
SearchResponse goodResponse =responses[0].getResponse().getResponse();
|
||||
assertNotNull(goodResponse);
|
||||
|
@ -930,18 +958,18 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
assertThat(goodResponse.getHits().getMaxScore(), greaterThan(0f));
|
||||
SearchHit hit = goodResponse.getHits().getHits()[0];
|
||||
assertNotNull(hit.getExplanation());
|
||||
assertFalse(goodResponse.getProfileResults().isEmpty());
|
||||
|
||||
|
||||
assertFalse(goodResponse.getProfileResults().isEmpty());
|
||||
|
||||
|
||||
assertNull(responses[0].getResponse().getSource());
|
||||
assertThat(responses[1].isFailure(), Matchers.is(true));
|
||||
assertNotNull(responses[1].getFailureMessage());
|
||||
assertNotNull(responses[1].getFailureMessage());
|
||||
assertThat(responses[1].getFailureMessage(), containsString("json_parse_exception"));
|
||||
}
|
||||
|
||||
|
||||
public void testMultiSearchTemplateAllBad() throws Exception {
|
||||
MultiSearchTemplateRequest multiSearchTemplateRequest = new MultiSearchTemplateRequest();
|
||||
|
||||
|
||||
SearchTemplateRequest badRequest1 = new SearchTemplateRequest();
|
||||
badRequest1.setRequest(new SearchRequest("index"));
|
||||
badRequest1.setScriptType(ScriptType.INLINE);
|
||||
|
@ -957,8 +985,8 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
scriptParams.put("number", "BAD NUMBER");
|
||||
badRequest1.setScriptParams(scriptParams);
|
||||
multiSearchTemplateRequest.add(badRequest1);
|
||||
|
||||
|
||||
|
||||
|
||||
SearchTemplateRequest badRequest2 = new SearchTemplateRequest();
|
||||
badRequest2.setRequest(new SearchRequest("index"));
|
||||
badRequest2.setScriptType(ScriptType.INLINE);
|
||||
|
@ -967,13 +995,13 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
scriptParams.put("number", "BAD NUMBER");
|
||||
badRequest2.setScriptParams(scriptParams);
|
||||
|
||||
multiSearchTemplateRequest.add(badRequest2);
|
||||
|
||||
// The whole HTTP request should fail if no nested search requests are valid
|
||||
multiSearchTemplateRequest.add(badRequest2);
|
||||
|
||||
// The whole HTTP request should fail if no nested search requests are valid
|
||||
ElasticsearchStatusException exception = expectThrows(ElasticsearchStatusException.class,
|
||||
() -> execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
|
||||
() -> execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
|
||||
highLevelClient()::multiSearchTemplateAsync));
|
||||
|
||||
|
||||
assertEquals(RestStatus.BAD_REQUEST, exception.status());
|
||||
assertThat(exception.getMessage(), containsString("no requests added"));
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequ
|
|||
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse;
|
||||
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
|
||||
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
|
||||
|
@ -43,6 +46,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class SnapshotIT extends ESRestHighLevelClientTestCase {
|
||||
|
||||
|
@ -173,6 +177,34 @@ public class SnapshotIT extends ESRestHighLevelClientTestCase {
|
|||
contains("test_snapshot1", "test_snapshot2"));
|
||||
}
|
||||
|
||||
public void testSnapshotsStatus() throws IOException {
|
||||
String testRepository = "test";
|
||||
String testSnapshot = "snapshot";
|
||||
String testIndex = "test_index";
|
||||
|
||||
PutRepositoryResponse putRepositoryResponse = createTestRepository(testRepository, FsRepository.TYPE, "{\"location\": \".\"}");
|
||||
assertTrue(putRepositoryResponse.isAcknowledged());
|
||||
|
||||
createIndex(testIndex, Settings.EMPTY);
|
||||
|
||||
CreateSnapshotRequest createSnapshotRequest = new CreateSnapshotRequest(testRepository, testSnapshot);
|
||||
createSnapshotRequest.indices(testIndex);
|
||||
createSnapshotRequest.waitForCompletion(true);
|
||||
CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
|
||||
// check that the request went ok without parsing JSON here. When using the high level client, check acknowledgement instead.
|
||||
assertEquals(RestStatus.OK, createSnapshotResponse.status());
|
||||
|
||||
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
|
||||
request.repository(testRepository);
|
||||
request.snapshots(new String[]{testSnapshot});
|
||||
SnapshotsStatusResponse response = execute(request, highLevelClient().snapshot()::status,
|
||||
highLevelClient().snapshot()::statusAsync);
|
||||
assertThat(response.getSnapshots().size(), equalTo(1));
|
||||
assertThat(response.getSnapshots().get(0).getSnapshot().getRepository(), equalTo(testRepository));
|
||||
assertThat(response.getSnapshots().get(0).getSnapshot().getSnapshotId().getName(), equalTo(testSnapshot));
|
||||
assertThat(response.getSnapshots().get(0).getIndices().containsKey(testIndex), is(true));
|
||||
}
|
||||
|
||||
public void testDeleteSnapshot() throws IOException {
|
||||
String repository = "test_repository";
|
||||
String snapshot = "test_snapshot";
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
package org.elasticsearch.client.documentation;
|
||||
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.nio.entity.NStringEntity;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.DocWriteRequest;
|
||||
|
@ -66,7 +64,6 @@ import org.elasticsearch.script.Script;
|
|||
import org.elasticsearch.script.ScriptType;
|
||||
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -756,7 +753,9 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
public void testGet() throws Exception {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
{
|
||||
String mappings = "{\n" +
|
||||
Request createIndex = new Request("PUT", "/posts");
|
||||
createIndex.setJsonEntity(
|
||||
"{\n" +
|
||||
" \"mappings\" : {\n" +
|
||||
" \"doc\" : {\n" +
|
||||
" \"properties\" : {\n" +
|
||||
|
@ -767,10 +766,8 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
|
||||
NStringEntity entity = new NStringEntity(mappings, ContentType.APPLICATION_JSON);
|
||||
Response response = client().performRequest("PUT", "/posts", Collections.emptyMap(), entity);
|
||||
"}");
|
||||
Response response = client().performRequest(createIndex);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
|
||||
IndexRequest indexRequest = new IndexRequest("posts", "doc", "1")
|
||||
|
@ -1071,21 +1068,21 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
RestHighLevelClient client = highLevelClient();
|
||||
|
||||
{
|
||||
String mappings = "{\n" +
|
||||
" \"mappings\" : {\n" +
|
||||
" \"type\" : {\n" +
|
||||
" \"properties\" : {\n" +
|
||||
" \"foo\" : {\n" +
|
||||
" \"type\": \"text\",\n" +
|
||||
" \"store\": true\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
|
||||
NStringEntity entity = new NStringEntity(mappings, ContentType.APPLICATION_JSON);
|
||||
Response response = client().performRequest("PUT", "/index", Collections.emptyMap(), entity);
|
||||
Request createIndex = new Request("PUT", "/index");
|
||||
createIndex.setJsonEntity(
|
||||
"{\n" +
|
||||
" \"mappings\" : {\n" +
|
||||
" \"type\" : {\n" +
|
||||
" \"properties\" : {\n" +
|
||||
" \"foo\" : {\n" +
|
||||
" \"type\": \"text\",\n" +
|
||||
" \"store\": true\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
Response response = client().performRequest(createIndex);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
|
|
|
@ -22,14 +22,24 @@ package org.elasticsearch.client.documentation;
|
|||
import org.apache.http.HttpHost;
|
||||
import org.elasticsearch.Build;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.LatchedActionListener;
|
||||
import org.elasticsearch.action.main.MainResponse;
|
||||
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse.BuildInfo;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse.LicenseInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Documentation for miscellaneous APIs in the high level java client.
|
||||
|
@ -66,6 +76,59 @@ public class MiscellaneousDocumentationIT extends ESRestHighLevelClientTestCase
|
|||
assertTrue(response);
|
||||
}
|
||||
|
||||
public void testXPackInfo() throws Exception {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
{
|
||||
//tag::x-pack-info-execute
|
||||
XPackInfoRequest request = new XPackInfoRequest();
|
||||
request.setVerbose(true); // <1>
|
||||
request.setCategories(EnumSet.of( // <2>
|
||||
XPackInfoRequest.Category.BUILD,
|
||||
XPackInfoRequest.Category.LICENSE,
|
||||
XPackInfoRequest.Category.FEATURES));
|
||||
XPackInfoResponse response = client.xpack().info(request, RequestOptions.DEFAULT);
|
||||
//end::x-pack-info-execute
|
||||
|
||||
//tag::x-pack-info-response
|
||||
BuildInfo build = response.getBuildInfo(); // <1>
|
||||
LicenseInfo license = response.getLicenseInfo(); // <2>
|
||||
assertEquals(XPackInfoResponse.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS,
|
||||
license.getExpiryDate()); // <3>
|
||||
FeatureSetsInfo features = response.getFeatureSetsInfo(); // <4>
|
||||
//end::x-pack-info-response
|
||||
|
||||
assertNotNull(response.getBuildInfo());
|
||||
assertNotNull(response.getLicenseInfo());
|
||||
assertNotNull(response.getFeatureSetsInfo());
|
||||
}
|
||||
{
|
||||
XPackInfoRequest request = new XPackInfoRequest();
|
||||
// tag::x-pack-info-execute-listener
|
||||
ActionListener<XPackInfoResponse> listener = new ActionListener<XPackInfoResponse>() {
|
||||
@Override
|
||||
public void onResponse(XPackInfoResponse indexResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
// end::x-pack-info-execute-listener
|
||||
|
||||
// Replace the empty listener by a blocking listener in test
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
// tag::x-pack-info-execute-async
|
||||
client.xpack().infoAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::x-pack-info-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
public void testInitializationFromClientBuilder() throws IOException {
|
||||
//tag::rest-high-level-client-init
|
||||
RestHighLevelClient client = new RestHighLevelClient(
|
||||
|
|
|
@ -37,11 +37,16 @@ import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
|||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotResponse;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStats;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStatus;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
|
||||
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
|
||||
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.Response;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.cluster.SnapshotsInProgress;
|
||||
import org.elasticsearch.cluster.metadata.RepositoryMetaData;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
@ -84,8 +89,8 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase {
|
||||
|
||||
private static final String repositoryName = "test_repository";
|
||||
|
||||
private static final String snapshotName = "test_snapshot";
|
||||
private static final String indexName = "test_index";
|
||||
|
||||
public void testSnapshotCreateRepository() throws IOException {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
|
@ -466,6 +471,7 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
|
|||
RestHighLevelClient client = highLevelClient();
|
||||
|
||||
createTestRepositories();
|
||||
createTestIndex();
|
||||
createTestSnapshots();
|
||||
|
||||
// tag::get-snapshots-request
|
||||
|
@ -543,10 +549,84 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
|
|||
}
|
||||
}
|
||||
|
||||
public void testSnapshotSnapshotsStatus() throws IOException {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
createTestRepositories();
|
||||
createTestIndex();
|
||||
createTestSnapshots();
|
||||
|
||||
// tag::snapshots-status-request
|
||||
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
|
||||
// end::snapshots-status-request
|
||||
|
||||
// tag::snapshots-status-request-repository
|
||||
request.repository(repositoryName); // <1>
|
||||
// end::snapshots-status-request-repository
|
||||
// tag::snapshots-status-request-snapshots
|
||||
String [] snapshots = new String[] {snapshotName};
|
||||
request.snapshots(snapshots); // <1>
|
||||
// end::snapshots-status-request-snapshots
|
||||
// tag::snapshots-status-request-ignoreUnavailable
|
||||
request.ignoreUnavailable(true); // <1>
|
||||
// end::snapshots-status-request-ignoreUnavailable
|
||||
// tag::snapshots-status-request-masterTimeout
|
||||
request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
|
||||
request.masterNodeTimeout("1m"); // <2>
|
||||
// end::snapshots-status-request-masterTimeout
|
||||
|
||||
// tag::snapshots-status-execute
|
||||
SnapshotsStatusResponse response = client.snapshot().status(request, RequestOptions.DEFAULT);
|
||||
// end::snapshots-status-execute
|
||||
|
||||
// tag::snapshots-status-response
|
||||
List<SnapshotStatus> snapshotStatusesResponse = response.getSnapshots();
|
||||
SnapshotStatus snapshotStatus = snapshotStatusesResponse.get(0); // <1>
|
||||
SnapshotsInProgress.State snapshotState = snapshotStatus.getState(); // <2>
|
||||
SnapshotStats shardStats = snapshotStatus.getIndices().get(indexName).getShards().get(0).getStats(); // <3>
|
||||
// end::snapshots-status-response
|
||||
assertThat(snapshotStatusesResponse.size(), equalTo(1));
|
||||
assertThat(snapshotStatusesResponse.get(0).getSnapshot().getRepository(), equalTo(SnapshotClientDocumentationIT.repositoryName));
|
||||
assertThat(snapshotStatusesResponse.get(0).getSnapshot().getSnapshotId().getName(), equalTo(snapshotName));
|
||||
assertThat(snapshotState.completed(), equalTo(true));
|
||||
}
|
||||
|
||||
public void testSnapshotSnapshotsStatusAsync() throws InterruptedException {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
{
|
||||
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
|
||||
|
||||
// tag::snapshots-status-execute-listener
|
||||
ActionListener<SnapshotsStatusResponse> listener =
|
||||
new ActionListener<SnapshotsStatusResponse>() {
|
||||
@Override
|
||||
public void onResponse(SnapshotsStatusResponse snapshotsStatusResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
// end::snapshots-status-execute-listener
|
||||
|
||||
// Replace the empty listener with a blocking listener in test
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
// tag::snapshots-status-execute-async
|
||||
client.snapshot().statusAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::snapshots-status-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
public void testSnapshotDeleteSnapshot() throws IOException {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
|
||||
createTestRepositories();
|
||||
createTestIndex();
|
||||
createTestSnapshots();
|
||||
|
||||
// tag::delete-snapshot-request
|
||||
|
@ -608,9 +688,14 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
|
|||
assertTrue(highLevelClient().snapshot().createRepository(request, RequestOptions.DEFAULT).isAcknowledged());
|
||||
}
|
||||
|
||||
private void createTestIndex() throws IOException {
|
||||
createIndex(indexName, Settings.EMPTY);
|
||||
}
|
||||
|
||||
private void createTestSnapshots() throws IOException {
|
||||
Request createSnapshot = new Request("put", String.format(Locale.ROOT, "_snapshot/%s/%s", repositoryName, snapshotName));
|
||||
createSnapshot.addParameter("wait_for_completion", "true");
|
||||
createSnapshot.setJsonEntity("{\"indices\":\"" + indexName + "\"}");
|
||||
Response response = highLevelClient().getLowLevelClient().performRequest(createSnapshot);
|
||||
// check that the request went ok without parsing JSON here. When using the high level client, check acknowledgement instead.
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
|
|
|
@ -72,11 +72,14 @@ public class RestClientBuilderIntegTests extends RestClientTestCase {
|
|||
}
|
||||
|
||||
public void testBuilderUsesDefaultSSLContext() throws Exception {
|
||||
assumeFalse("Due to bug inside jdk, this test can't momentarily run with java 11. " +
|
||||
"See: https://github.com/elastic/elasticsearch/issues/31940",
|
||||
System.getProperty("java.version").contains("11"));
|
||||
final SSLContext defaultSSLContext = SSLContext.getDefault();
|
||||
try {
|
||||
try (RestClient client = buildRestClient()) {
|
||||
try {
|
||||
client.performRequest("GET", "/");
|
||||
client.performRequest(new Request("GET", "/"));
|
||||
fail("connection should have been rejected due to SSL handshake");
|
||||
} catch (Exception e) {
|
||||
assertThat(e.getMessage(), containsString("General SSLEngine problem"));
|
||||
|
@ -85,7 +88,7 @@ public class RestClientBuilderIntegTests extends RestClientTestCase {
|
|||
|
||||
SSLContext.setDefault(getSslContext());
|
||||
try (RestClient client = buildRestClient()) {
|
||||
Response response = client.performRequest("GET", "/");
|
||||
Response response = client.performRequest(new Request("GET", "/"));
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -256,35 +256,51 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
|
|||
|
||||
public void testEncodeParams() throws IOException {
|
||||
{
|
||||
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "this/is/the/routing"));
|
||||
Request request = new Request("PUT", "/200");
|
||||
request.addParameter("routing", "this/is/the/routing");
|
||||
Response response = restClient.performRequest(request);
|
||||
assertEquals(pathPrefix + "/200?routing=this%2Fis%2Fthe%2Frouting", response.getRequestLine().getUri());
|
||||
}
|
||||
{
|
||||
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "this|is|the|routing"));
|
||||
Request request = new Request("PUT", "/200");
|
||||
request.addParameter("routing", "this|is|the|routing");
|
||||
Response response = restClient.performRequest(request);
|
||||
assertEquals(pathPrefix + "/200?routing=this%7Cis%7Cthe%7Crouting", response.getRequestLine().getUri());
|
||||
}
|
||||
{
|
||||
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "routing#1"));
|
||||
Request request = new Request("PUT", "/200");
|
||||
request.addParameter("routing", "routing#1");
|
||||
Response response = restClient.performRequest(request);
|
||||
assertEquals(pathPrefix + "/200?routing=routing%231", response.getRequestLine().getUri());
|
||||
}
|
||||
{
|
||||
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "中文"));
|
||||
Request request = new Request("PUT", "/200");
|
||||
request.addParameter("routing", "中文");
|
||||
Response response = restClient.performRequest(request);
|
||||
assertEquals(pathPrefix + "/200?routing=%E4%B8%AD%E6%96%87", response.getRequestLine().getUri());
|
||||
}
|
||||
{
|
||||
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "foo bar"));
|
||||
Request request = new Request("PUT", "/200");
|
||||
request.addParameter("routing", "foo bar");
|
||||
Response response = restClient.performRequest(request);
|
||||
assertEquals(pathPrefix + "/200?routing=foo+bar", response.getRequestLine().getUri());
|
||||
}
|
||||
{
|
||||
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "foo+bar"));
|
||||
Request request = new Request("PUT", "/200");
|
||||
request.addParameter("routing", "foo+bar");
|
||||
Response response = restClient.performRequest(request);
|
||||
assertEquals(pathPrefix + "/200?routing=foo%2Bbar", response.getRequestLine().getUri());
|
||||
}
|
||||
{
|
||||
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "foo/bar"));
|
||||
Request request = new Request("PUT", "/200");
|
||||
request.addParameter("routing", "foo/bar");
|
||||
Response response = restClient.performRequest(request);
|
||||
assertEquals(pathPrefix + "/200?routing=foo%2Fbar", response.getRequestLine().getUri());
|
||||
}
|
||||
{
|
||||
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "foo^bar"));
|
||||
Request request = new Request("PUT", "/200");
|
||||
request.addParameter("routing", "foo^bar");
|
||||
Response response = restClient.performRequest(request);
|
||||
assertEquals(pathPrefix + "/200?routing=foo%5Ebar", response.getRequestLine().getUri());
|
||||
}
|
||||
}
|
||||
|
@ -341,14 +357,14 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
|
|||
public void testUrlWithoutLeadingSlash() throws Exception {
|
||||
if (pathPrefix.length() == 0) {
|
||||
try {
|
||||
restClient.performRequest("GET", "200");
|
||||
restClient.performRequest(new Request("GET", "200"));
|
||||
fail("request should have failed");
|
||||
} catch (ResponseException e) {
|
||||
assertEquals(404, e.getResponse().getStatusLine().getStatusCode());
|
||||
}
|
||||
} else {
|
||||
{
|
||||
Response response = restClient.performRequest("GET", "200");
|
||||
Response response = restClient.performRequest(new Request("GET", "200"));
|
||||
//a trailing slash gets automatically added if a pathPrefix is configured
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
@ -357,7 +373,7 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
|
|||
try (RestClient restClient = RestClient.builder(
|
||||
new HttpHost(httpServer.getAddress().getHostString(), httpServer.getAddress().getPort()))
|
||||
.setPathPrefix(pathPrefix.substring(1)).build()) {
|
||||
Response response = restClient.performRequest("GET", "200");
|
||||
Response response = restClient.performRequest(new Request("GET", "200"));
|
||||
//a trailing slash gets automatically added if a pathPrefix is configured
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ public class RestClientDocumentation {
|
|||
}
|
||||
{
|
||||
//tag::rest-client-response2
|
||||
Response response = restClient.performRequest("GET", "/");
|
||||
Response response = restClient.performRequest(new Request("GET", "/"));
|
||||
RequestLine requestLine = response.getRequestLine(); // <1>
|
||||
HttpHost host = response.getHost(); // <2>
|
||||
int statusCode = response.getStatusLine().getStatusCode(); // <3>
|
||||
|
|
|
@ -46,6 +46,12 @@ my %Group_Labels = (
|
|||
'other' => 'NOT CLASSIFIED',
|
||||
);
|
||||
|
||||
my %Area_Overrides = (
|
||||
':ml' => 'Machine Learning',
|
||||
':beats' => 'Beats Plugin',
|
||||
':Docs' => 'Docs Infrastructure'
|
||||
);
|
||||
|
||||
use JSON();
|
||||
use Encode qw(encode_utf8);
|
||||
|
||||
|
@ -175,8 +181,14 @@ ISSUE:
|
|||
# uncomment for including/excluding PRs already issued in other versions
|
||||
# next if grep {$_->{name}=~/^v2/} @{$issue->{labels}};
|
||||
my %labels = map { $_->{name} => 1 } @{ $issue->{labels} };
|
||||
my ($header) = map { m{:[^/]+/(.+)} && $1 }
|
||||
grep {/^:/} sort keys %labels;
|
||||
my @area_labels = grep {/^:/} sort keys %labels;
|
||||
my ($header) = map { m{:[^/]+/(.+)} && $1 } @area_labels;
|
||||
if (scalar @area_labels > 1) {
|
||||
$header = "MULTIPLE AREA LABELS";
|
||||
}
|
||||
if (scalar @area_labels == 1 && exists $Area_Overrides{$area_labels[0]}) {
|
||||
$header = $Area_Overrides{$area_labels[0]};
|
||||
}
|
||||
$header ||= 'NOT CLASSIFIED';
|
||||
for (@Groups) {
|
||||
if ( $labels{$_} ) {
|
||||
|
|
|
@ -130,12 +130,11 @@ include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[rest-high-level-cl
|
|||
--------------------------------------------------
|
||||
|
||||
The high-level client will internally create the low-level client used to
|
||||
perform requests based on the provided builder, and manage its lifecycle.
|
||||
|
||||
The high-level client instance needs to be closed when no longer needed so that
|
||||
all the resources used by it get properly released, as well as the underlying
|
||||
http client instance and its threads. This can be done through the `close`
|
||||
method, which will close the internal `RestClient` instance.
|
||||
perform requests based on the provided builder. That low-level client
|
||||
maintains a pool of connections and starts some threads so you should
|
||||
close the high-level client when you are well and truly done with
|
||||
it and it will in turn close the internal low-level client to free those
|
||||
resources. This can be done through the `close`:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
[[java-rest-high-x-pack-info]]
|
||||
=== X-Pack Info API
|
||||
|
||||
[[java-rest-high-x-pack-info-execution]]
|
||||
==== Execution
|
||||
|
||||
General information about the installed {xpack} features can be retrieved
|
||||
using the `xPackInfo()` method:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute]
|
||||
--------------------------------------------------
|
||||
<1> Enable verbose mode. The default is `false` but `true` will return
|
||||
more information.
|
||||
<2> Set the categories of information to retrieve. The the default is to
|
||||
return no information which is useful for checking if {xpack} is installed
|
||||
but not much else.
|
||||
|
||||
[[java-rest-high-x-pack-info-response]]
|
||||
==== Response
|
||||
|
||||
The returned `XPackInfoResponse` can contain `BuildInfo`, `LicenseInfo`,
|
||||
and `FeatureSetsInfo` depending on the categories requested.
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-response]
|
||||
--------------------------------------------------
|
||||
<1> `BuildInfo` contains the commit hash from which Elasticsearch was
|
||||
built and the timestamp that the x-pack module was created.
|
||||
<2> `LicenseInfo` contains the type of license that the cluster is using
|
||||
and its expiration date.
|
||||
<3> Basic licenses do not expire and will return this constant.
|
||||
<4> `FeatureSetsInfo` contains a `Map` from the name of a feature to
|
||||
information about a feature like whether or not it is available under
|
||||
the current license.
|
||||
|
||||
[[java-rest-high-x-pack-info-async]]
|
||||
==== Asynchronous Execution
|
||||
|
||||
This request can be executed asynchronously:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute-async]
|
||||
--------------------------------------------------
|
||||
<1> The `XPackInfoRequest` to execute and the `ActionListener` to use when
|
||||
the execution completes
|
||||
|
||||
The asynchronous method does not block and returns immediately. Once it is
|
||||
completed the `ActionListener` is called back using the `onResponse` method
|
||||
if the execution successfully completed or using the `onFailure` method if
|
||||
it failed.
|
||||
|
||||
A typical listener for `XPackInfoResponse` looks like:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute-listener]
|
||||
--------------------------------------------------
|
||||
<1> Called when the execution is successfully completed. The response is
|
||||
provided as an argument
|
||||
<2> Called in case of failure. The raised exception is provided as an argument
|
|
@ -0,0 +1,97 @@
|
|||
[[java-rest-high-snapshot-snapshots-status]]
|
||||
=== Snapshots Status API
|
||||
|
||||
The Snapshots Status API allows to retrieve detailed information about snapshots in progress.
|
||||
|
||||
[[java-rest-high-snapshot-snapshots-status-request]]
|
||||
==== Snapshots Status Request
|
||||
|
||||
A `SnapshotsStatusRequest`:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request]
|
||||
--------------------------------------------------
|
||||
|
||||
==== Required Arguments
|
||||
The following arguments must be provided:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-repository]
|
||||
--------------------------------------------------
|
||||
<1> Sets the repository to check for snapshot statuses
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-snapshots]
|
||||
--------------------------------------------------
|
||||
<1> The list of snapshot names to check the status of
|
||||
|
||||
==== Optional Arguments
|
||||
The following arguments can optionally be provided:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-ignoreUnavailable]
|
||||
--------------------------------------------------
|
||||
<1> The command will fail if some of the snapshots are unavailable. The `ignore_unavailable` flag
|
||||
set to true will return all snapshots that are currently available.
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-masterTimeout]
|
||||
--------------------------------------------------
|
||||
<1> Timeout to connect to the master node as a `TimeValue`
|
||||
<2> Timeout to connect to the master node as a `String`
|
||||
|
||||
[[java-rest-high-snapshot-snapshots-status-sync]]
|
||||
==== Synchronous Execution
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute]
|
||||
--------------------------------------------------
|
||||
|
||||
[[java-rest-high-snapshot-snapshots-status-async]]
|
||||
==== Asynchronous Execution
|
||||
|
||||
The asynchronous execution of retrieving snapshot statuses requires both the
|
||||
`SnapshotsStatusRequest` instance and an `ActionListener` instance to be
|
||||
passed to the asynchronous method:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute-async]
|
||||
--------------------------------------------------
|
||||
<1> The `SnapshotsStatusRequest` to execute and the `ActionListener`
|
||||
to use when the execution completes
|
||||
|
||||
The asynchronous method does not block and returns immediately. Once it is
|
||||
completed the `ActionListener` is called back using the `onResponse` method
|
||||
if the execution successfully completed or using the `onFailure` method if
|
||||
it failed.
|
||||
|
||||
A typical listener for `SnapshotsStatusResponse` looks like:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute-listener]
|
||||
--------------------------------------------------
|
||||
<1> Called when the execution is successfully completed. The response is
|
||||
provided as an argument
|
||||
<2> Called in case of a failure. The raised exception is provided as an argument
|
||||
|
||||
[[java-rest-high-snapshot-snapshots-status-response]]
|
||||
==== Snapshots Status Response
|
||||
|
||||
The returned `SnapshotsStatusResponse` allows to retrieve information about the
|
||||
executed operation as follows:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-response]
|
||||
--------------------------------------------------
|
||||
<1> Request contains a list of snapshot statuses
|
||||
<2> Each status contains information about the snapshot
|
||||
<3> Example of reading snapshot statistics about a specific index and shard
|
|
@ -53,9 +53,11 @@ The Java High Level REST Client supports the following Miscellaneous APIs:
|
|||
|
||||
* <<java-rest-high-main>>
|
||||
* <<java-rest-high-ping>>
|
||||
* <<java-rest-high-x-pack-info>>
|
||||
|
||||
include::miscellaneous/main.asciidoc[]
|
||||
include::miscellaneous/ping.asciidoc[]
|
||||
include::miscellaneous/x-pack-info.asciidoc[]
|
||||
|
||||
== Indices APIs
|
||||
|
||||
|
@ -152,6 +154,7 @@ The Java High Level REST Client supports the following Snapshot APIs:
|
|||
* <<java-rest-high-snapshot-verify-repository>>
|
||||
* <<java-rest-high-snapshot-create-snapshot>>
|
||||
* <<java-rest-high-snapshot-get-snapshots>>
|
||||
* <<java-rest-high-snapshot-snapshots-status>>
|
||||
* <<java-rest-high-snapshot-delete-snapshot>>
|
||||
|
||||
include::snapshot/get_repository.asciidoc[]
|
||||
|
@ -160,6 +163,7 @@ include::snapshot/delete_repository.asciidoc[]
|
|||
include::snapshot/verify_repository.asciidoc[]
|
||||
include::snapshot/create_snapshot.asciidoc[]
|
||||
include::snapshot/get_snapshots.asciidoc[]
|
||||
include::snapshot/snapshots_status.asciidoc[]
|
||||
include::snapshot/delete_snapshot.asciidoc[]
|
||||
|
||||
== Tasks APIs
|
||||
|
@ -181,4 +185,3 @@ The Java High Level REST Client supports the following Scripts APIs:
|
|||
|
||||
include::script/get_script.asciidoc[]
|
||||
include::script/delete_script.asciidoc[]
|
||||
|
||||
|
|
|
@ -119,6 +119,30 @@ GET hockey/_search
|
|||
----------------------------------------------------------------
|
||||
// CONSOLE
|
||||
|
||||
|
||||
[float]
|
||||
===== Missing values
|
||||
|
||||
If you request the value from a field `field` that isn’t in
|
||||
the document, `doc['field'].value` for this document returns:
|
||||
|
||||
- `0` if a `field` has a numeric datatype (long, double etc.)
|
||||
- `false` is a `field` has a boolean datatype
|
||||
- epoch date if a `field` has a date datatype
|
||||
- `null` if a `field` has a string datatype
|
||||
- `null` if a `field` has a geo datatype
|
||||
- `""` if a `field` has a binary datatype
|
||||
|
||||
IMPORTANT: Starting in 7.0, `doc['field'].value` throws an exception if
|
||||
the field is missing in a document. To enable this behavior now,
|
||||
set a {ref}/jvm-options.html[`jvm.option`]
|
||||
`-Des.scripting.exception_for_missing_value=true` on a node. If you do not enable
|
||||
this behavior, a deprecation warning is logged on start up.
|
||||
|
||||
To check if a document is missing a value, you can call
|
||||
`doc['field'].size() == 0`.
|
||||
|
||||
|
||||
[float]
|
||||
==== Updating Fields with Painless
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ Available expressions for interval: `year` (`1y`), `quarter` (`1q`), `month` (`1
|
|||
Time values can also be specified via abbreviations supported by <<time-units,time units>> parsing.
|
||||
Note that fractional time values are not supported, but you can address this by shifting to another
|
||||
time unit (e.g., `1.5h` could instead be specified as `90m`). Also note that time intervals larger than
|
||||
than days do not support arbitrary values but can only be one unit large (e.g. `1y` is valid, `2y` is not).
|
||||
days do not support arbitrary values but can only be one unit large (e.g. `1y` is valid, `2y` is not).
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
|
|
|
@ -50,7 +50,49 @@ PUT /test_index
|
|||
The above configures a `search_synonyms` filter, with a path of
|
||||
`analysis/synonym.txt` (relative to the `config` location). The
|
||||
`search_synonyms` analyzer is then configured with the filter.
|
||||
Additional settings are: `expand` (defaults to `true`).
|
||||
|
||||
Additional settings are:
|
||||
|
||||
* `expand` (defaults to `true`).
|
||||
* `lenient` (defaults to `false`). If `true` ignores exceptions while parsing the synonym configuration. It is important
|
||||
to note that only those synonym rules which cannot get parsed are ignored. For instance consider the following request:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
PUT /test_index
|
||||
{
|
||||
"settings": {
|
||||
"index" : {
|
||||
"analysis" : {
|
||||
"analyzer" : {
|
||||
"synonym" : {
|
||||
"tokenizer" : "standard",
|
||||
"filter" : ["my_stop", "synonym_graph"]
|
||||
}
|
||||
},
|
||||
"filter" : {
|
||||
"my_stop": {
|
||||
"type" : "stop",
|
||||
"stopwords": ["bar"]
|
||||
},
|
||||
"synonym_graph" : {
|
||||
"type" : "synonym_graph",
|
||||
"lenient": true,
|
||||
"synonyms" : ["foo, bar => baz"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
With the above request the word `bar` gets skipped but a mapping `foo => baz` is still added. However, if the mapping
|
||||
being added was "foo, baz => bar" nothing would get added to the synonym list. This is because the target word for the
|
||||
mapping is itself eliminated because it was a stop word. Similarly, if the mapping was "bar, foo, baz" and `expand` was
|
||||
set to `false` no mapping would get added as when `expand=false` the target mapping is the first word. However, if
|
||||
`expand=true` then the mappings added would be equivalent to `foo, baz => foo, baz` i.e, all mappings other than the
|
||||
stop word.
|
||||
|
||||
[float]
|
||||
==== `tokenizer` and `ignore_case` are deprecated
|
||||
|
|
|
@ -33,12 +33,55 @@ PUT /test_index
|
|||
|
||||
The above configures a `synonym` filter, with a path of
|
||||
`analysis/synonym.txt` (relative to the `config` location). The
|
||||
`synonym` analyzer is then configured with the filter. Additional
|
||||
settings is: `expand` (defaults to `true`).
|
||||
`synonym` analyzer is then configured with the filter.
|
||||
|
||||
This filter tokenize synonyms with whatever tokenizer and token filters
|
||||
appear before it in the chain.
|
||||
|
||||
Additional settings are:
|
||||
|
||||
* `expand` (defaults to `true`).
|
||||
* `lenient` (defaults to `false`). If `true` ignores exceptions while parsing the synonym configuration. It is important
|
||||
to note that only those synonym rules which cannot get parsed are ignored. For instance consider the following request:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
PUT /test_index
|
||||
{
|
||||
"settings": {
|
||||
"index" : {
|
||||
"analysis" : {
|
||||
"analyzer" : {
|
||||
"synonym" : {
|
||||
"tokenizer" : "standard",
|
||||
"filter" : ["my_stop", "synonym"]
|
||||
}
|
||||
},
|
||||
"filter" : {
|
||||
"my_stop": {
|
||||
"type" : "stop",
|
||||
"stopwords": ["bar"]
|
||||
},
|
||||
"synonym" : {
|
||||
"type" : "synonym",
|
||||
"lenient": true,
|
||||
"synonyms" : ["foo, bar => baz"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
With the above request the word `bar` gets skipped but a mapping `foo => baz` is still added. However, if the mapping
|
||||
being added was "foo, baz => bar" nothing would get added to the synonym list. This is because the target word for the
|
||||
mapping is itself eliminated because it was a stop word. Similarly, if the mapping was "bar, foo, baz" and `expand` was
|
||||
set to `false` no mapping would get added as when `expand=false` the target mapping is the first word. However, if
|
||||
`expand=true` then the mappings added would be equivalent to `foo, baz => foo, baz` i.e, all mappings other than the
|
||||
stop word.
|
||||
|
||||
|
||||
[float]
|
||||
==== `tokenizer` and `ignore_case` are deprecated
|
||||
|
||||
|
|
|
@ -104,6 +104,11 @@ With that out of the way, let's get started with the fun part...
|
|||
|
||||
== Installation
|
||||
|
||||
You can skip installation completely by using our hosted
|
||||
Elasticsearch Service on https://www.elastic.co/cloud[Elastic Cloud], which is
|
||||
available on AWS and GCP. You can
|
||||
https://www.elastic.co/cloud/elasticsearch-service/signup[try out the hosted service] for free.
|
||||
|
||||
Elasticsearch requires at least Java 8. Specifically as of this writing, it is recommended that you use the Oracle JDK version {jdk}. Java installation varies from platform to platform so we won't go into those details here. Oracle's recommended installation documentation can be found on http://docs.oracle.com/javase/8/docs/technotes/guides/install/install_overview.html[Oracle's website]. Suffice to say, before you install Elasticsearch, please check your Java version first by running (and then install/upgrade accordingly if needed):
|
||||
|
||||
[source,sh]
|
||||
|
|
|
@ -1767,8 +1767,9 @@ Removes existing fields. If one field doesn't exist, an exception will be thrown
|
|||
.Remove Options
|
||||
[options="header"]
|
||||
|======
|
||||
| Name | Required | Default | Description
|
||||
| `field` | yes | - | Fields to be removed
|
||||
| Name | Required | Default | Description
|
||||
| `field` | yes | - | Fields to be removed
|
||||
| `ignore_missing` | no | `false` | If `true` and `field` does not exist or is `null`, the processor quietly exits without modifying the document
|
||||
|======
|
||||
|
||||
Here is an example to remove a single field:
|
||||
|
|
|
@ -85,6 +85,13 @@ for a particular index with the index setting `index.max_regex_length`.
|
|||
Search requests with extra content after the main object will no longer be accepted
|
||||
by the `_search` endpoint. A parsing exception will be thrown instead.
|
||||
|
||||
==== Context Completion Suggester
|
||||
|
||||
The ability to query and index context enabled suggestions without context,
|
||||
deprecated in 6.x, has been removed. Context enabled suggestion queries
|
||||
without contexts have to visit every suggestion, which degrades the search performance
|
||||
considerably.
|
||||
|
||||
==== Semantics changed for `max_concurrent_shard_requests`
|
||||
|
||||
`max_concurrent_shard_requests` used to limit the total number of concurrent shard
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
[[install-elasticsearch]]
|
||||
== Installing Elasticsearch
|
||||
|
||||
Elasticsearch can be run on your own hardware or using our hosted
|
||||
Elasticsearch Service on https://www.elastic.co/cloud[Elastic Cloud], which is
|
||||
available on AWS and GCP. You can
|
||||
https://www.elastic.co/cloud/elasticsearch-service/signup[try out the hosted service] for free.
|
||||
|
||||
Elasticsearch is provided in the following package formats:
|
||||
|
||||
[horizontal]
|
||||
|
@ -38,7 +43,7 @@ Elasticsearch on Windows. MSIs may be downloaded from the Elasticsearch website.
|
|||
`docker`::
|
||||
|
||||
Images are available for running Elasticsearch as Docker containers. They may be
|
||||
downloaded from the Elastic Docker Registry.
|
||||
downloaded from the Elastic Docker Registry.
|
||||
+
|
||||
{ref}/docker.html[Install {es} with Docker]
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ A list of all published Docker images and tags can be found in
|
|||
https://www.docker.elastic.co[www.docker.elastic.co]. The source code can be found
|
||||
on https://github.com/elastic/elasticsearch-docker/tree/{branch}[GitHub].
|
||||
|
||||
==== Image types
|
||||
|
||||
These images are free to use under the Elastic license. They contain open source
|
||||
and free commercial features and access to paid commercial features.
|
||||
{xpack-ref}/license-management.html[Start a 30-day trial] to try out all of the
|
||||
|
@ -17,9 +15,6 @@ paid commercial features. See the
|
|||
https://www.elastic.co/subscriptions[Subscriptions] page for information about
|
||||
Elastic license levels.
|
||||
|
||||
Alternatively, you can download `-oss` images, which contain only features that
|
||||
are available under the Apache 2.0 license.
|
||||
|
||||
==== Pulling the image
|
||||
|
||||
Obtaining {es} for Docker is as simple as issuing a +docker pull+ command
|
||||
|
@ -34,14 +29,17 @@ endif::[]
|
|||
|
||||
ifeval::["{release-state}"!="unreleased"]
|
||||
|
||||
Docker images can be retrieved with the following commands:
|
||||
For example, the Docker image can be retrieved with the following command:
|
||||
|
||||
["source","sh",subs="attributes"]
|
||||
--------------------------------------------
|
||||
docker pull {docker-repo}:{version}
|
||||
docker pull {docker-repo}-oss:{version}
|
||||
--------------------------------------------
|
||||
|
||||
Alternatively, you can download other Docker images that contain only features
|
||||
that are available under the Apache 2.0 license from
|
||||
https://www.docker.elastic.co[www.docker.elastic.co].
|
||||
|
||||
endif::[]
|
||||
|
||||
[[docker-cli-run]]
|
||||
|
|
|
@ -34,10 +34,6 @@ ifeval::["{release-state}"!="unreleased"]
|
|||
|
||||
Download the `.msi` package for Elasticsearch v{version} from https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-{version}.msi
|
||||
|
||||
Alternatively, you can download the following package, which contains only
|
||||
features that are available under the Apache 2.0 license:
|
||||
https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-oss-{version}.msi
|
||||
|
||||
endif::[]
|
||||
|
||||
[[install-msi-gui]]
|
||||
|
|
|
@ -21,12 +21,19 @@ package org.elasticsearch.nio;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BytesChannelContext extends SocketChannelContext {
|
||||
|
||||
public BytesChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
|
||||
ReadWriteHandler handler, InboundChannelBuffer channelBuffer) {
|
||||
super(channel, selector, exceptionHandler, handler, channelBuffer);
|
||||
this(channel, selector, exceptionHandler, handler, channelBuffer, ALWAYS_ALLOW_CHANNEL);
|
||||
}
|
||||
|
||||
public BytesChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
|
||||
ReadWriteHandler handler, InboundChannelBuffer channelBuffer,
|
||||
Predicate<NioSocketChannel> allowChannelPredicate) {
|
||||
super(channel, selector, exceptionHandler, handler, channelBuffer, allowChannelPredicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,7 +84,7 @@ public class BytesChannelContext extends SocketChannelContext {
|
|||
|
||||
@Override
|
||||
public boolean selectorShouldClose() {
|
||||
return isPeerClosed() || hasIOException() || isClosing.get();
|
||||
return closeNow() || isClosing.get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,6 +47,11 @@ public abstract class ChannelContext<S extends SelectableChannel & NetworkChanne
|
|||
}
|
||||
|
||||
protected void register() throws IOException {
|
||||
doSelectorRegister();
|
||||
}
|
||||
|
||||
// Package private for testing
|
||||
void doSelectorRegister() throws IOException {
|
||||
setSelectionKey(rawChannel.register(getSelector().rawSelector(), 0));
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.LinkedList;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* This context should implement the specific logic for a channel. When a channel receives a notification
|
||||
|
@ -43,24 +44,28 @@ import java.util.function.Consumer;
|
|||
*/
|
||||
public abstract class SocketChannelContext extends ChannelContext<SocketChannel> {
|
||||
|
||||
public static final Predicate<NioSocketChannel> ALWAYS_ALLOW_CHANNEL = (c) -> true;
|
||||
|
||||
protected final NioSocketChannel channel;
|
||||
protected final InboundChannelBuffer channelBuffer;
|
||||
protected final AtomicBoolean isClosing = new AtomicBoolean(false);
|
||||
private final ReadWriteHandler readWriteHandler;
|
||||
private final Predicate<NioSocketChannel> allowChannelPredicate;
|
||||
private final NioSelector selector;
|
||||
private final CompletableContext<Void> connectContext = new CompletableContext<>();
|
||||
private final LinkedList<FlushOperation> pendingFlushes = new LinkedList<>();
|
||||
private boolean ioException;
|
||||
private boolean peerClosed;
|
||||
private boolean closeNow;
|
||||
private Exception connectException;
|
||||
|
||||
protected SocketChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
|
||||
ReadWriteHandler readWriteHandler, InboundChannelBuffer channelBuffer) {
|
||||
ReadWriteHandler readWriteHandler, InboundChannelBuffer channelBuffer,
|
||||
Predicate<NioSocketChannel> allowChannelPredicate) {
|
||||
super(channel.getRawChannel(), exceptionHandler);
|
||||
this.selector = selector;
|
||||
this.channel = channel;
|
||||
this.readWriteHandler = readWriteHandler;
|
||||
this.channelBuffer = channelBuffer;
|
||||
this.allowChannelPredicate = allowChannelPredicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -161,6 +166,14 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
|
|||
return pendingFlushes.peekFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void register() throws IOException {
|
||||
super.register();
|
||||
if (allowChannelPredicate.test(channel) == false) {
|
||||
closeNow = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeFromSelector() throws IOException {
|
||||
getSelector().assertOnSelectorThread();
|
||||
|
@ -217,24 +230,20 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
|
|||
*/
|
||||
public abstract boolean selectorShouldClose();
|
||||
|
||||
protected boolean hasIOException() {
|
||||
return ioException;
|
||||
}
|
||||
|
||||
protected boolean isPeerClosed() {
|
||||
return peerClosed;
|
||||
protected boolean closeNow() {
|
||||
return closeNow;
|
||||
}
|
||||
|
||||
protected int readFromChannel(ByteBuffer buffer) throws IOException {
|
||||
try {
|
||||
int bytesRead = rawChannel.read(buffer);
|
||||
if (bytesRead < 0) {
|
||||
peerClosed = true;
|
||||
closeNow = true;
|
||||
bytesRead = 0;
|
||||
}
|
||||
return bytesRead;
|
||||
} catch (IOException e) {
|
||||
ioException = true;
|
||||
closeNow = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -243,12 +252,12 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
|
|||
try {
|
||||
int bytesRead = (int) rawChannel.read(buffers);
|
||||
if (bytesRead < 0) {
|
||||
peerClosed = true;
|
||||
closeNow = true;
|
||||
bytesRead = 0;
|
||||
}
|
||||
return bytesRead;
|
||||
} catch (IOException e) {
|
||||
ioException = true;
|
||||
closeNow = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +266,7 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
|
|||
try {
|
||||
return rawChannel.write(buffer);
|
||||
} catch (IOException e) {
|
||||
ioException = true;
|
||||
closeNow = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +275,7 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
|
|||
try {
|
||||
return (int) rawChannel.write(buffers);
|
||||
} catch (IOException e) {
|
||||
ioException = true;
|
||||
closeNow = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
|
@ -77,7 +78,7 @@ public class SocketChannelContextTests extends ESTestCase {
|
|||
when(rawChannel.write(any(ByteBuffer.class))).thenThrow(new IOException());
|
||||
when(rawChannel.read(any(ByteBuffer[].class), anyInt(), anyInt())).thenThrow(new IOException());
|
||||
when(rawChannel.read(any(ByteBuffer.class))).thenThrow(new IOException());
|
||||
assertFalse(context.hasIOException());
|
||||
assertFalse(context.closeNow());
|
||||
expectThrows(IOException.class, () -> {
|
||||
if (randomBoolean()) {
|
||||
context.read();
|
||||
|
@ -85,15 +86,31 @@ public class SocketChannelContextTests extends ESTestCase {
|
|||
context.flushChannel();
|
||||
}
|
||||
});
|
||||
assertTrue(context.hasIOException());
|
||||
assertTrue(context.closeNow());
|
||||
}
|
||||
|
||||
public void testSignalWhenPeerClosed() throws IOException {
|
||||
when(rawChannel.read(any(ByteBuffer[].class), anyInt(), anyInt())).thenReturn(-1L);
|
||||
when(rawChannel.read(any(ByteBuffer.class))).thenReturn(-1);
|
||||
assertFalse(context.isPeerClosed());
|
||||
assertFalse(context.closeNow());
|
||||
context.read();
|
||||
assertTrue(context.isPeerClosed());
|
||||
assertTrue(context.closeNow());
|
||||
}
|
||||
|
||||
public void testValidateInRegisterCanSucceed() throws IOException {
|
||||
InboundChannelBuffer channelBuffer = InboundChannelBuffer.allocatingInstance();
|
||||
context = new TestSocketChannelContext(channel, selector, exceptionHandler, readWriteHandler, channelBuffer, (c) -> true);
|
||||
assertFalse(context.closeNow());
|
||||
context.register();
|
||||
assertFalse(context.closeNow());
|
||||
}
|
||||
|
||||
public void testValidateInRegisterCanFail() throws IOException {
|
||||
InboundChannelBuffer channelBuffer = InboundChannelBuffer.allocatingInstance();
|
||||
context = new TestSocketChannelContext(channel, selector, exceptionHandler, readWriteHandler, channelBuffer, (c) -> false);
|
||||
assertFalse(context.closeNow());
|
||||
context.register();
|
||||
assertTrue(context.closeNow());
|
||||
}
|
||||
|
||||
public void testConnectSucceeds() throws IOException {
|
||||
|
@ -277,7 +294,13 @@ public class SocketChannelContextTests extends ESTestCase {
|
|||
|
||||
private TestSocketChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
|
||||
ReadWriteHandler readWriteHandler, InboundChannelBuffer channelBuffer) {
|
||||
super(channel, selector, exceptionHandler, readWriteHandler, channelBuffer);
|
||||
this(channel, selector, exceptionHandler, readWriteHandler, channelBuffer, ALWAYS_ALLOW_CHANNEL);
|
||||
}
|
||||
|
||||
private TestSocketChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
|
||||
ReadWriteHandler readWriteHandler, InboundChannelBuffer channelBuffer,
|
||||
Predicate<NioSocketChannel> allowChannelPredicate) {
|
||||
super(channel, selector, exceptionHandler, readWriteHandler, channelBuffer, allowChannelPredicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -309,6 +332,11 @@ public class SocketChannelContextTests extends ESTestCase {
|
|||
public void closeChannel() {
|
||||
isClosing.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
void doSelectorRegister() {
|
||||
// We do not want to call the actual register with selector method as it will throw a NPE
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] createMessage(int length) {
|
||||
|
|
|
@ -294,6 +294,7 @@ public final class ConstructingObjectParser<Value, Context> extends AbstractObje
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return objectParser.getName();
|
||||
}
|
||||
|
|
|
@ -411,6 +411,7 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
|
|||
INT_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING),
|
||||
BOOLEAN_ARRAY(START_ARRAY, VALUE_BOOLEAN),
|
||||
OBJECT(START_OBJECT),
|
||||
OBJECT_OR_NULL(START_OBJECT, VALUE_NULL),
|
||||
OBJECT_ARRAY(START_OBJECT, START_ARRAY),
|
||||
OBJECT_OR_BOOLEAN(START_OBJECT, VALUE_BOOLEAN),
|
||||
OBJECT_OR_STRING(START_OBJECT, VALUE_STRING),
|
||||
|
|
|
@ -32,6 +32,8 @@ import org.elasticsearch.ingest.AbstractProcessor;
|
|||
import org.elasticsearch.ingest.ConfigurationUtils;
|
||||
import org.elasticsearch.ingest.IngestDocument;
|
||||
import org.elasticsearch.ingest.Processor;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.TemplateScript;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
|
@ -42,21 +44,22 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
|
|||
public static final String TYPE = "date_index_name";
|
||||
|
||||
private final String field;
|
||||
private final String indexNamePrefix;
|
||||
private final String dateRounding;
|
||||
private final String indexNameFormat;
|
||||
private final TemplateScript.Factory indexNamePrefixTemplate;
|
||||
private final TemplateScript.Factory dateRoundingTemplate;
|
||||
private final TemplateScript.Factory indexNameFormatTemplate;
|
||||
private final DateTimeZone timezone;
|
||||
private final List<Function<String, DateTime>> dateFormats;
|
||||
|
||||
DateIndexNameProcessor(String tag, String field, List<Function<String, DateTime>> dateFormats, DateTimeZone timezone,
|
||||
String indexNamePrefix, String dateRounding, String indexNameFormat) {
|
||||
TemplateScript.Factory indexNamePrefixTemplate, TemplateScript.Factory dateRoundingTemplate,
|
||||
TemplateScript.Factory indexNameFormatTemplate) {
|
||||
super(tag);
|
||||
this.field = field;
|
||||
this.timezone = timezone;
|
||||
this.dateFormats = dateFormats;
|
||||
this.indexNamePrefix = indexNamePrefix;
|
||||
this.dateRounding = dateRounding;
|
||||
this.indexNameFormat = indexNameFormat;
|
||||
this.indexNamePrefixTemplate = indexNamePrefixTemplate;
|
||||
this.dateRoundingTemplate = dateRoundingTemplate;
|
||||
this.indexNameFormatTemplate = indexNameFormatTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,6 +86,9 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
|
|||
if (dateTime == null) {
|
||||
throw new IllegalArgumentException("unable to parse date [" + date + "]", lastException);
|
||||
}
|
||||
String indexNamePrefix = ingestDocument.renderTemplate(indexNamePrefixTemplate);
|
||||
String indexNameFormat = ingestDocument.renderTemplate(indexNameFormatTemplate);
|
||||
String dateRounding = ingestDocument.renderTemplate(dateRoundingTemplate);
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormat.forPattern(indexNameFormat);
|
||||
StringBuilder builder = new StringBuilder()
|
||||
|
@ -106,16 +112,16 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
|
|||
return field;
|
||||
}
|
||||
|
||||
String getIndexNamePrefix() {
|
||||
return indexNamePrefix;
|
||||
TemplateScript.Factory getIndexNamePrefixTemplate() {
|
||||
return indexNamePrefixTemplate;
|
||||
}
|
||||
|
||||
String getDateRounding() {
|
||||
return dateRounding;
|
||||
TemplateScript.Factory getDateRoundingTemplate() {
|
||||
return dateRoundingTemplate;
|
||||
}
|
||||
|
||||
String getIndexNameFormat() {
|
||||
return indexNameFormat;
|
||||
TemplateScript.Factory getIndexNameFormatTemplate() {
|
||||
return indexNameFormatTemplate;
|
||||
}
|
||||
|
||||
DateTimeZone getTimezone() {
|
||||
|
@ -128,6 +134,12 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
|
|||
|
||||
public static final class Factory implements Processor.Factory {
|
||||
|
||||
private final ScriptService scriptService;
|
||||
|
||||
public Factory(ScriptService scriptService) {
|
||||
this.scriptService = scriptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DateIndexNameProcessor create(Map<String, Processor.Factory> registry, String tag,
|
||||
Map<String, Object> config) throws Exception {
|
||||
|
@ -154,9 +166,16 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
|
|||
|
||||
String field = ConfigurationUtils.readStringProperty(TYPE, tag, config, "field");
|
||||
String indexNamePrefix = ConfigurationUtils.readStringProperty(TYPE, tag, config, "index_name_prefix", "");
|
||||
TemplateScript.Factory indexNamePrefixTemplate =
|
||||
ConfigurationUtils.compileTemplate(TYPE, tag, "index_name_prefix", indexNamePrefix, scriptService);
|
||||
String dateRounding = ConfigurationUtils.readStringProperty(TYPE, tag, config, "date_rounding");
|
||||
TemplateScript.Factory dateRoundingTemplate =
|
||||
ConfigurationUtils.compileTemplate(TYPE, tag, "date_rounding", dateRounding, scriptService);
|
||||
String indexNameFormat = ConfigurationUtils.readStringProperty(TYPE, tag, config, "index_name_format", "yyyy-MM-dd");
|
||||
return new DateIndexNameProcessor(tag, field, dateFormats, timezone, indexNamePrefix, dateRounding, indexNameFormat);
|
||||
TemplateScript.Factory indexNameFormatTemplate =
|
||||
ConfigurationUtils.compileTemplate(TYPE, tag, "index_name_format", indexNameFormat, scriptService);
|
||||
return new DateIndexNameProcessor(tag, field, dateFormats, timezone, indexNamePrefixTemplate,
|
||||
dateRoundingTemplate, indexNameFormatTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ public class IngestCommonPlugin extends Plugin implements ActionPlugin, IngestPl
|
|||
processors.put(DateProcessor.TYPE, new DateProcessor.Factory(parameters.scriptService));
|
||||
processors.put(SetProcessor.TYPE, new SetProcessor.Factory(parameters.scriptService));
|
||||
processors.put(AppendProcessor.TYPE, new AppendProcessor.Factory(parameters.scriptService));
|
||||
processors.put(RenameProcessor.TYPE, new RenameProcessor.Factory());
|
||||
processors.put(RenameProcessor.TYPE, new RenameProcessor.Factory(parameters.scriptService));
|
||||
processors.put(RemoveProcessor.TYPE, new RemoveProcessor.Factory(parameters.scriptService));
|
||||
processors.put(SplitProcessor.TYPE, new SplitProcessor.Factory());
|
||||
processors.put(JoinProcessor.TYPE, new JoinProcessor.Factory());
|
||||
|
@ -73,7 +73,7 @@ public class IngestCommonPlugin extends Plugin implements ActionPlugin, IngestPl
|
|||
processors.put(GsubProcessor.TYPE, new GsubProcessor.Factory());
|
||||
processors.put(FailProcessor.TYPE, new FailProcessor.Factory(parameters.scriptService));
|
||||
processors.put(ForEachProcessor.TYPE, new ForEachProcessor.Factory());
|
||||
processors.put(DateIndexNameProcessor.TYPE, new DateIndexNameProcessor.Factory());
|
||||
processors.put(DateIndexNameProcessor.TYPE, new DateIndexNameProcessor.Factory(parameters.scriptService));
|
||||
processors.put(SortProcessor.TYPE, new SortProcessor.Factory());
|
||||
processors.put(GrokProcessor.TYPE, new GrokProcessor.Factory(GROK_PATTERNS, createGrokThreadWatchdog(parameters)));
|
||||
processors.put(ScriptProcessor.TYPE, new ScriptProcessor.Factory(parameters.scriptService));
|
||||
|
@ -97,12 +97,12 @@ public class IngestCommonPlugin extends Plugin implements ActionPlugin, IngestPl
|
|||
Supplier<DiscoveryNodes> nodesInCluster) {
|
||||
return Arrays.asList(new GrokProcessorGetAction.RestAction(settings, restController));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Setting<?>> getSettings() {
|
||||
return Arrays.asList(WATCHDOG_INTERVAL, WATCHDOG_MAX_EXECUTION_TIME);
|
||||
}
|
||||
|
||||
|
||||
private static ThreadWatchdog createGrokThreadWatchdog(Processor.Parameters parameters) {
|
||||
long intervalMillis = WATCHDOG_INTERVAL.get(parameters.env.settings()).getMillis();
|
||||
long maxExecutionTimeMillis = WATCHDOG_MAX_EXECUTION_TIME.get(parameters.env.settings()).getMillis();
|
||||
|
|
|
@ -39,10 +39,12 @@ public final class RemoveProcessor extends AbstractProcessor {
|
|||
public static final String TYPE = "remove";
|
||||
|
||||
private final List<TemplateScript.Factory> fields;
|
||||
private final boolean ignoreMissing;
|
||||
|
||||
RemoveProcessor(String tag, List<TemplateScript.Factory> fields) {
|
||||
RemoveProcessor(String tag, List<TemplateScript.Factory> fields, boolean ignoreMissing) {
|
||||
super(tag);
|
||||
this.fields = new ArrayList<>(fields);
|
||||
this.ignoreMissing = ignoreMissing;
|
||||
}
|
||||
|
||||
public List<TemplateScript.Factory> getFields() {
|
||||
|
@ -51,7 +53,16 @@ public final class RemoveProcessor extends AbstractProcessor {
|
|||
|
||||
@Override
|
||||
public void execute(IngestDocument document) {
|
||||
fields.forEach(document::removeField);
|
||||
if (ignoreMissing) {
|
||||
fields.forEach(field -> {
|
||||
String path = document.renderTemplate(field);
|
||||
if (document.hasField(path)) {
|
||||
document.removeField(path);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fields.forEach(document::removeField);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,7 +94,8 @@ public final class RemoveProcessor extends AbstractProcessor {
|
|||
final List<TemplateScript.Factory> compiledTemplates = fields.stream()
|
||||
.map(f -> ConfigurationUtils.compileTemplate(TYPE, processorTag, "field", f, scriptService))
|
||||
.collect(Collectors.toList());
|
||||
return new RemoveProcessor(processorTag, compiledTemplates);
|
||||
boolean ignoreMissing = ConfigurationUtils.readBooleanProperty(TYPE, processorTag, config, "ignore_missing", false);
|
||||
return new RemoveProcessor(processorTag, compiledTemplates, ignoreMissing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import org.elasticsearch.ingest.AbstractProcessor;
|
|||
import org.elasticsearch.ingest.ConfigurationUtils;
|
||||
import org.elasticsearch.ingest.IngestDocument;
|
||||
import org.elasticsearch.ingest.Processor;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.TemplateScript;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -33,22 +35,22 @@ public final class RenameProcessor extends AbstractProcessor {
|
|||
|
||||
public static final String TYPE = "rename";
|
||||
|
||||
private final String field;
|
||||
private final String targetField;
|
||||
private final TemplateScript.Factory field;
|
||||
private final TemplateScript.Factory targetField;
|
||||
private final boolean ignoreMissing;
|
||||
|
||||
RenameProcessor(String tag, String field, String targetField, boolean ignoreMissing) {
|
||||
RenameProcessor(String tag, TemplateScript.Factory field, TemplateScript.Factory targetField, boolean ignoreMissing) {
|
||||
super(tag);
|
||||
this.field = field;
|
||||
this.targetField = targetField;
|
||||
this.ignoreMissing = ignoreMissing;
|
||||
}
|
||||
|
||||
String getField() {
|
||||
TemplateScript.Factory getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
String getTargetField() {
|
||||
TemplateScript.Factory getTargetField() {
|
||||
return targetField;
|
||||
}
|
||||
|
||||
|
@ -58,28 +60,30 @@ public final class RenameProcessor extends AbstractProcessor {
|
|||
|
||||
@Override
|
||||
public void execute(IngestDocument document) {
|
||||
if (document.hasField(field, true) == false) {
|
||||
String path = document.renderTemplate(field);
|
||||
if (document.hasField(path, true) == false) {
|
||||
if (ignoreMissing) {
|
||||
return;
|
||||
} else {
|
||||
throw new IllegalArgumentException("field [" + field + "] doesn't exist");
|
||||
throw new IllegalArgumentException("field [" + path + "] doesn't exist");
|
||||
}
|
||||
}
|
||||
// We fail here if the target field point to an array slot that is out of range.
|
||||
// If we didn't do this then we would fail if we set the value in the target_field
|
||||
// and then on failure processors would not see that value we tried to rename as we already
|
||||
// removed it.
|
||||
if (document.hasField(targetField, true)) {
|
||||
throw new IllegalArgumentException("field [" + targetField + "] already exists");
|
||||
String target = document.renderTemplate(targetField);
|
||||
if (document.hasField(target, true)) {
|
||||
throw new IllegalArgumentException("field [" + target + "] already exists");
|
||||
}
|
||||
|
||||
Object value = document.getFieldValue(field, Object.class);
|
||||
document.removeField(field);
|
||||
Object value = document.getFieldValue(path, Object.class);
|
||||
document.removeField(path);
|
||||
try {
|
||||
document.setFieldValue(targetField, value);
|
||||
document.setFieldValue(target, value);
|
||||
} catch (Exception e) {
|
||||
// setting the value back to the original field shouldn't as we just fetched it from that field:
|
||||
document.setFieldValue(field, value);
|
||||
document.setFieldValue(path, value);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -90,13 +94,24 @@ public final class RenameProcessor extends AbstractProcessor {
|
|||
}
|
||||
|
||||
public static final class Factory implements Processor.Factory {
|
||||
|
||||
private final ScriptService scriptService;
|
||||
|
||||
public Factory(ScriptService scriptService) {
|
||||
this.scriptService = scriptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenameProcessor create(Map<String, Processor.Factory> registry, String processorTag,
|
||||
Map<String, Object> config) throws Exception {
|
||||
String field = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "field");
|
||||
TemplateScript.Factory fieldTemplate = ConfigurationUtils.compileTemplate(TYPE, processorTag,
|
||||
"field", field, scriptService);
|
||||
String targetField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "target_field");
|
||||
TemplateScript.Factory targetFieldTemplate = ConfigurationUtils.compileTemplate(TYPE, processorTag,
|
||||
"target_field", targetField, scriptService);
|
||||
boolean ignoreMissing = ConfigurationUtils.readBooleanProperty(TYPE, processorTag, config, "ignore_missing", false);
|
||||
return new RenameProcessor(processorTag, field, targetField, ignoreMissing);
|
||||
return new RenameProcessor(processorTag, fieldTemplate, targetFieldTemplate , ignoreMissing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,18 +20,20 @@
|
|||
package org.elasticsearch.ingest.common;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.ingest.TestTemplateService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DateIndexNameFactoryTests extends ESTestCase {
|
||||
|
||||
public void testDefaults() throws Exception {
|
||||
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory();
|
||||
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory(TestTemplateService.instance());
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("field", "_field");
|
||||
config.put("date_rounding", "y");
|
||||
|
@ -39,14 +41,14 @@ public class DateIndexNameFactoryTests extends ESTestCase {
|
|||
DateIndexNameProcessor processor = factory.create(null, null, config);
|
||||
assertThat(processor.getDateFormats().size(), Matchers.equalTo(1));
|
||||
assertThat(processor.getField(), Matchers.equalTo("_field"));
|
||||
assertThat(processor.getIndexNamePrefix(), Matchers.equalTo(""));
|
||||
assertThat(processor.getDateRounding(), Matchers.equalTo("y"));
|
||||
assertThat(processor.getIndexNameFormat(), Matchers.equalTo("yyyy-MM-dd"));
|
||||
assertThat(processor.getIndexNamePrefixTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo(""));
|
||||
assertThat(processor.getDateRoundingTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo("y"));
|
||||
assertThat(processor.getIndexNameFormatTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo("yyyy-MM-dd"));
|
||||
assertThat(processor.getTimezone(), Matchers.equalTo(DateTimeZone.UTC));
|
||||
}
|
||||
|
||||
public void testSpecifyOptionalSettings() throws Exception {
|
||||
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory();
|
||||
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory(TestTemplateService.instance());
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("field", "_field");
|
||||
config.put("index_name_prefix", "_prefix");
|
||||
|
@ -63,7 +65,7 @@ public class DateIndexNameFactoryTests extends ESTestCase {
|
|||
config.put("index_name_format", "yyyyMMdd");
|
||||
|
||||
processor = factory.create(null, null, config);
|
||||
assertThat(processor.getIndexNameFormat(), Matchers.equalTo("yyyyMMdd"));
|
||||
assertThat(processor.getIndexNameFormatTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo("yyyyMMdd"));
|
||||
|
||||
config = new HashMap<>();
|
||||
config.put("field", "_field");
|
||||
|
@ -80,11 +82,11 @@ public class DateIndexNameFactoryTests extends ESTestCase {
|
|||
config.put("date_rounding", "y");
|
||||
|
||||
processor = factory.create(null, null, config);
|
||||
assertThat(processor.getIndexNamePrefix(), Matchers.equalTo("_prefix"));
|
||||
assertThat(processor.getIndexNamePrefixTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo("_prefix"));
|
||||
}
|
||||
|
||||
public void testRequiredFields() throws Exception {
|
||||
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory();
|
||||
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory(TestTemplateService.instance());
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("date_rounding", "y");
|
||||
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, null, config));
|
||||
|
@ -95,5 +97,4 @@ public class DateIndexNameFactoryTests extends ESTestCase {
|
|||
e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, null, config));
|
||||
assertThat(e.getMessage(), Matchers.equalTo("[date_rounding] required property is missing"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,11 +19,14 @@
|
|||
package org.elasticsearch.ingest.common;
|
||||
|
||||
import org.elasticsearch.ingest.IngestDocument;
|
||||
import org.elasticsearch.ingest.TestTemplateService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -33,11 +36,8 @@ public class DateIndexNameProcessorTests extends ESTestCase {
|
|||
|
||||
public void testJodaPattern() throws Exception {
|
||||
Function<String, DateTime> function = DateFormat.Joda.getFunction("yyyy-MM-dd'T'HH:mm:ss.SSSZ", DateTimeZone.UTC, Locale.ROOT);
|
||||
DateIndexNameProcessor processor = new DateIndexNameProcessor(
|
||||
"_tag", "_field", Collections.singletonList(function), DateTimeZone.UTC,
|
||||
"events-", "y", "yyyyMMdd"
|
||||
);
|
||||
|
||||
DateIndexNameProcessor processor = createProcessor("_field", Collections.singletonList(function),
|
||||
DateTimeZone.UTC, "events-", "y", "yyyyMMdd");
|
||||
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
|
||||
Collections.singletonMap("_field", "2016-04-25T12:24:20.101Z"));
|
||||
processor.execute(document);
|
||||
|
@ -46,7 +46,7 @@ public class DateIndexNameProcessorTests extends ESTestCase {
|
|||
|
||||
public void testTAI64N()throws Exception {
|
||||
Function<String, DateTime> function = DateFormat.Tai64n.getFunction(null, DateTimeZone.UTC, null);
|
||||
DateIndexNameProcessor dateProcessor = new DateIndexNameProcessor("_tag", "_field", Collections.singletonList(function),
|
||||
DateIndexNameProcessor dateProcessor = createProcessor("_field", Collections.singletonList(function),
|
||||
DateTimeZone.UTC, "events-", "m", "yyyyMMdd");
|
||||
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
|
||||
Collections.singletonMap("_field", (randomBoolean() ? "@" : "") + "4000000050d506482dbdf024"));
|
||||
|
@ -56,7 +56,7 @@ public class DateIndexNameProcessorTests extends ESTestCase {
|
|||
|
||||
public void testUnixMs()throws Exception {
|
||||
Function<String, DateTime> function = DateFormat.UnixMs.getFunction(null, DateTimeZone.UTC, null);
|
||||
DateIndexNameProcessor dateProcessor = new DateIndexNameProcessor("_tag", "_field", Collections.singletonList(function),
|
||||
DateIndexNameProcessor dateProcessor = createProcessor("_field", Collections.singletonList(function),
|
||||
DateTimeZone.UTC, "events-", "m", "yyyyMMdd");
|
||||
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
|
||||
Collections.singletonMap("_field", "1000500"));
|
||||
|
@ -71,7 +71,7 @@ public class DateIndexNameProcessorTests extends ESTestCase {
|
|||
|
||||
public void testUnix()throws Exception {
|
||||
Function<String, DateTime> function = DateFormat.Unix.getFunction(null, DateTimeZone.UTC, null);
|
||||
DateIndexNameProcessor dateProcessor = new DateIndexNameProcessor("_tag", "_field", Collections.singletonList(function),
|
||||
DateIndexNameProcessor dateProcessor = createProcessor("_field", Collections.singletonList(function),
|
||||
DateTimeZone.UTC, "events-", "m", "yyyyMMdd");
|
||||
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
|
||||
Collections.singletonMap("_field", "1000.5"));
|
||||
|
@ -79,4 +79,33 @@ public class DateIndexNameProcessorTests extends ESTestCase {
|
|||
assertThat(document.getSourceAndMetadata().get("_index"), equalTo("<events-{19700101||/m{yyyyMMdd|UTC}}>"));
|
||||
}
|
||||
|
||||
public void testTemplatedFields() throws Exception {
|
||||
String indexNamePrefix = randomAlphaOfLength(10);
|
||||
String dateRounding = randomFrom("y", "M", "w", "d", "h", "m", "s");
|
||||
String indexNameFormat = randomFrom("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyyMMdd", "MM/dd/yyyy");
|
||||
String date = Integer.toString(randomInt());
|
||||
Function<String, DateTime> dateTimeFunction = DateFormat.Unix.getFunction(null, DateTimeZone.UTC, null);
|
||||
|
||||
DateIndexNameProcessor dateProcessor = createProcessor("_field",
|
||||
Collections.singletonList(dateTimeFunction), DateTimeZone.UTC, indexNamePrefix,
|
||||
dateRounding, indexNameFormat);
|
||||
|
||||
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
|
||||
Collections.singletonMap("_field", date));
|
||||
dateProcessor.execute(document);
|
||||
|
||||
assertThat(document.getSourceAndMetadata().get("_index"),
|
||||
equalTo("<"+indexNamePrefix+"{"+DateTimeFormat.forPattern(indexNameFormat)
|
||||
.print(dateTimeFunction.apply(date))+"||/"+dateRounding+"{"+indexNameFormat+"|UTC}}>"));
|
||||
}
|
||||
|
||||
private DateIndexNameProcessor createProcessor(String field, List<Function<String, DateTime>> dateFormats,
|
||||
DateTimeZone timezone, String indexNamePrefix, String dateRounding,
|
||||
String indexNameFormat) {
|
||||
return new DateIndexNameProcessor(randomAlphaOfLength(10), field, dateFormats, timezone,
|
||||
new TestTemplateService.MockTemplateScript.Factory(indexNamePrefix),
|
||||
new TestTemplateService.MockTemplateScript.Factory(dateRounding),
|
||||
new TestTemplateService.MockTemplateScript.Factory(indexNameFormat)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,10 @@ import org.elasticsearch.ingest.RandomDocumentPicks;
|
|||
import org.elasticsearch.ingest.TestTemplateService;
|
||||
import org.elasticsearch.script.TemplateScript;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -36,19 +37,21 @@ import java.util.Map;
|
|||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
public class DateProcessorTests extends ESTestCase {
|
||||
|
||||
private TemplateScript.Factory templatize(Locale locale) {
|
||||
return new TestTemplateService.MockTemplateScript.Factory(locale.getLanguage());
|
||||
}
|
||||
|
||||
private TemplateScript.Factory templatize(DateTimeZone timezone) {
|
||||
return new TestTemplateService.MockTemplateScript.Factory(timezone.getID());
|
||||
private TemplateScript.Factory templatize(ZoneId timezone) {
|
||||
// prevent writing "UTC" as string, as joda time does not parse it
|
||||
String id = timezone.equals(ZoneOffset.UTC) ? "UTC" : timezone.getId();
|
||||
return new TestTemplateService.MockTemplateScript.Factory(id);
|
||||
}
|
||||
public void testJodaPattern() {
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
|
||||
templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH),
|
||||
templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH),
|
||||
"date_as_string", Collections.singletonList("yyyy dd MM hh:mm:ss"), "date_as_date");
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
document.put("date_as_string", "2010 12 06 11:05:15");
|
||||
|
@ -63,7 +66,7 @@ public class DateProcessorTests extends ESTestCase {
|
|||
matchFormats.add("dd/MM/yyyy");
|
||||
matchFormats.add("dd-MM-yyyy");
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
|
||||
templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH),
|
||||
templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH),
|
||||
"date_as_string", matchFormats, "date_as_date");
|
||||
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
|
@ -98,7 +101,7 @@ public class DateProcessorTests extends ESTestCase {
|
|||
public void testInvalidJodaPattern() {
|
||||
try {
|
||||
DateProcessor processor = new DateProcessor(randomAlphaOfLength(10),
|
||||
templatize(UTC), templatize(randomLocale(random())),
|
||||
templatize(ZoneOffset.UTC), templatize(randomLocale(random())),
|
||||
"date_as_string", Collections.singletonList("invalid pattern"), "date_as_date");
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
document.put("date_as_string", "2010");
|
||||
|
@ -112,7 +115,7 @@ public class DateProcessorTests extends ESTestCase {
|
|||
|
||||
public void testJodaPatternLocale() {
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
|
||||
templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ITALIAN),
|
||||
templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ITALIAN),
|
||||
"date_as_string", Collections.singletonList("yyyy dd MMM"), "date_as_date");
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
document.put("date_as_string", "2010 12 giugno");
|
||||
|
@ -123,18 +126,18 @@ public class DateProcessorTests extends ESTestCase {
|
|||
|
||||
public void testJodaPatternDefaultYear() {
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
|
||||
templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH),
|
||||
templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH),
|
||||
"date_as_string", Collections.singletonList("dd/MM"), "date_as_date");
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
document.put("date_as_string", "12/06");
|
||||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
|
||||
dateProcessor.execute(ingestDocument);
|
||||
assertThat(ingestDocument.getFieldValue("date_as_date", String.class),
|
||||
equalTo(DateTime.now().getYear() + "-06-12T00:00:00.000+02:00"));
|
||||
equalTo(ZonedDateTime.now().getYear() + "-06-12T00:00:00.000+02:00"));
|
||||
}
|
||||
|
||||
public void testTAI64N() {
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(DateTimeZone.forOffsetHours(2)),
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.ofHours(2)),
|
||||
templatize(randomLocale(random())),
|
||||
"date_as_string", Collections.singletonList("TAI64N"), "date_as_date");
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
|
@ -146,8 +149,8 @@ public class DateProcessorTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testUnixMs() {
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(UTC), templatize(randomLocale(random())),
|
||||
"date_as_string", Collections.singletonList("UNIX_MS"), "date_as_date");
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.UTC),
|
||||
templatize(randomLocale(random())), "date_as_string", Collections.singletonList("UNIX_MS"), "date_as_date");
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
document.put("date_as_string", "1000500");
|
||||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
|
||||
|
@ -162,7 +165,7 @@ public class DateProcessorTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testUnix() {
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(UTC),
|
||||
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.UTC),
|
||||
templatize(randomLocale(random())),
|
||||
"date_as_string", Collections.singletonList("UNIX"), "date_as_date");
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
|
@ -186,7 +189,7 @@ public class DateProcessorTests extends ESTestCase {
|
|||
|
||||
public void testInvalidLocale() {
|
||||
DateProcessor processor = new DateProcessor(randomAlphaOfLength(10),
|
||||
templatize(UTC), new TestTemplateService.MockTemplateScript.Factory("invalid_locale"),
|
||||
templatize(ZoneOffset.UTC), new TestTemplateService.MockTemplateScript.Factory("invalid_locale"),
|
||||
"date_as_string", Collections.singletonList("yyyy"), "date_as_date");
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
document.put("date_as_string", "2010");
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.ingest.common;
|
|||
|
||||
import org.elasticsearch.ingest.IngestDocument;
|
||||
import org.elasticsearch.ingest.Processor;
|
||||
import org.elasticsearch.ingest.TestTemplateService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -86,7 +87,8 @@ public class DotExpanderProcessorTests extends ESTestCase {
|
|||
// so because foo is no branch field but a value field the `foo.bar` field can't be expanded
|
||||
// into [foo].[bar], so foo should be renamed first into `[foo].[bar]:
|
||||
IngestDocument document = new IngestDocument(source, Collections.emptyMap());
|
||||
Processor processor = new RenameProcessor("_tag", "foo", "foo.bar", false);
|
||||
Processor processor = new RenameProcessor("_tag", new TestTemplateService.MockTemplateScript.Factory("foo"),
|
||||
new TestTemplateService.MockTemplateScript.Factory("foo.bar"), false);
|
||||
processor.execute(document);
|
||||
processor = new DotExpanderProcessor("_tag", null, "foo.bar");
|
||||
processor.execute(document);
|
||||
|
|
|
@ -129,7 +129,7 @@ public class GrokProcessorTests extends ESTestCase {
|
|||
public void testMissingFieldWithIgnoreMissing() throws Exception {
|
||||
String fieldName = "foo.bar";
|
||||
IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
|
||||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
|
||||
IngestDocument ingestDocument = new IngestDocument(originalIngestDocument);
|
||||
GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"),
|
||||
Collections.singletonList("%{ONE:one}"), fieldName, false, true, ThreadWatchdog.noop());
|
||||
processor.execute(ingestDocument);
|
||||
|
|
|
@ -33,7 +33,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.ingest.IngestDocumentMatcher.assertIngestDocument;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
|
@ -55,7 +54,7 @@ public class JsonProcessorTests extends ESTestCase {
|
|||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
|
||||
jsonProcessor.execute(ingestDocument);
|
||||
Map<String, Object> jsonified = ingestDocument.getFieldValue(randomTargetField, Map.class);
|
||||
assertIngestDocument(ingestDocument.getFieldValue(randomTargetField, Object.class), jsonified);
|
||||
assertEquals(ingestDocument.getFieldValue(randomTargetField, Object.class), jsonified);
|
||||
}
|
||||
|
||||
public void testInvalidValue() {
|
||||
|
@ -161,13 +160,10 @@ public class JsonProcessorTests extends ESTestCase {
|
|||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
|
||||
jsonProcessor.execute(ingestDocument);
|
||||
|
||||
Map<String, Object> expected = new HashMap<>();
|
||||
expected.put("a", 1);
|
||||
expected.put("b", 2);
|
||||
expected.put("c", "see");
|
||||
IngestDocument expectedIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), expected);
|
||||
|
||||
assertIngestDocument(ingestDocument, expectedIngestDocument);
|
||||
Map<String, Object> sourceAndMetadata = ingestDocument.getSourceAndMetadata();
|
||||
assertEquals(1, sourceAndMetadata.get("a"));
|
||||
assertEquals(2, sourceAndMetadata.get("b"));
|
||||
assertEquals("see", sourceAndMetadata.get("c"));
|
||||
}
|
||||
|
||||
public void testAddBoolToRoot() {
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.elasticsearch.test.ESTestCase;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
@ -37,7 +38,7 @@ public class RemoveProcessorTests extends ESTestCase {
|
|||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
|
||||
String field = RandomDocumentPicks.randomExistingFieldName(random(), ingestDocument);
|
||||
Processor processor = new RemoveProcessor(randomAlphaOfLength(10),
|
||||
Collections.singletonList(new TestTemplateService.MockTemplateScript.Factory(field)));
|
||||
Collections.singletonList(new TestTemplateService.MockTemplateScript.Factory(field)), false);
|
||||
processor.execute(ingestDocument);
|
||||
assertThat(ingestDocument.hasField(field), equalTo(false));
|
||||
}
|
||||
|
@ -45,8 +46,10 @@ public class RemoveProcessorTests extends ESTestCase {
|
|||
public void testRemoveNonExistingField() throws Exception {
|
||||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
|
||||
String fieldName = RandomDocumentPicks.randomFieldName(random());
|
||||
Processor processor = new RemoveProcessor(randomAlphaOfLength(10),
|
||||
Collections.singletonList(new TestTemplateService.MockTemplateScript.Factory(fieldName)));
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("field", fieldName);
|
||||
String processorTag = randomAlphaOfLength(10);
|
||||
Processor processor = new RemoveProcessor.Factory(TestTemplateService.instance()).create(null, processorTag, config);
|
||||
try {
|
||||
processor.execute(ingestDocument);
|
||||
fail("remove field should have failed");
|
||||
|
@ -54,4 +57,15 @@ public class RemoveProcessorTests extends ESTestCase {
|
|||
assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testIgnoreMissing() throws Exception {
|
||||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
|
||||
String fieldName = RandomDocumentPicks.randomFieldName(random());
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("field", fieldName);
|
||||
config.put("ignore_missing", true);
|
||||
String processorTag = randomAlphaOfLength(10);
|
||||
Processor processor = new RemoveProcessor.Factory(TestTemplateService.instance()).create(null, processorTag, config);
|
||||
processor.execute(ingestDocument);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,11 @@
|
|||
package org.elasticsearch.ingest.common;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.ingest.TestTemplateService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -29,21 +32,26 @@ import static org.hamcrest.CoreMatchers.equalTo;
|
|||
|
||||
public class RenameProcessorFactoryTests extends ESTestCase {
|
||||
|
||||
private RenameProcessor.Factory factory;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
factory = new RenameProcessor.Factory(TestTemplateService.instance());
|
||||
}
|
||||
|
||||
public void testCreate() throws Exception {
|
||||
RenameProcessor.Factory factory = new RenameProcessor.Factory();
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("field", "old_field");
|
||||
config.put("target_field", "new_field");
|
||||
String processorTag = randomAlphaOfLength(10);
|
||||
RenameProcessor renameProcessor = factory.create(null, processorTag, config);
|
||||
assertThat(renameProcessor.getTag(), equalTo(processorTag));
|
||||
assertThat(renameProcessor.getField(), equalTo("old_field"));
|
||||
assertThat(renameProcessor.getTargetField(), equalTo("new_field"));
|
||||
assertThat(renameProcessor.getField().newInstance(Collections.emptyMap()).execute(), equalTo("old_field"));
|
||||
assertThat(renameProcessor.getTargetField().newInstance(Collections.emptyMap()).execute(), equalTo("new_field"));
|
||||
assertThat(renameProcessor.isIgnoreMissing(), equalTo(false));
|
||||
}
|
||||
|
||||
public void testCreateWithIgnoreMissing() throws Exception {
|
||||
RenameProcessor.Factory factory = new RenameProcessor.Factory();
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("field", "old_field");
|
||||
config.put("target_field", "new_field");
|
||||
|
@ -51,13 +59,12 @@ public class RenameProcessorFactoryTests extends ESTestCase {
|
|||
String processorTag = randomAlphaOfLength(10);
|
||||
RenameProcessor renameProcessor = factory.create(null, processorTag, config);
|
||||
assertThat(renameProcessor.getTag(), equalTo(processorTag));
|
||||
assertThat(renameProcessor.getField(), equalTo("old_field"));
|
||||
assertThat(renameProcessor.getTargetField(), equalTo("new_field"));
|
||||
assertThat(renameProcessor.getField().newInstance(Collections.emptyMap()).execute(), equalTo("old_field"));
|
||||
assertThat(renameProcessor.getTargetField().newInstance(Collections.emptyMap()).execute(), equalTo("new_field"));
|
||||
assertThat(renameProcessor.isIgnoreMissing(), equalTo(true));
|
||||
}
|
||||
|
||||
public void testCreateNoFieldPresent() throws Exception {
|
||||
RenameProcessor.Factory factory = new RenameProcessor.Factory();
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("target_field", "new_field");
|
||||
try {
|
||||
|
@ -69,7 +76,6 @@ public class RenameProcessorFactoryTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testCreateNoToPresent() throws Exception {
|
||||
RenameProcessor.Factory factory = new RenameProcessor.Factory();
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("field", "old_field");
|
||||
try {
|
||||
|
|
|
@ -22,6 +22,7 @@ package org.elasticsearch.ingest.common;
|
|||
import org.elasticsearch.ingest.IngestDocument;
|
||||
import org.elasticsearch.ingest.Processor;
|
||||
import org.elasticsearch.ingest.RandomDocumentPicks;
|
||||
import org.elasticsearch.ingest.TestTemplateService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -45,7 +46,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
do {
|
||||
newFieldName = RandomDocumentPicks.randomFieldName(random());
|
||||
} while (RandomDocumentPicks.canAddField(newFieldName, ingestDocument) == false || newFieldName.equals(fieldName));
|
||||
Processor processor = new RenameProcessor(randomAlphaOfLength(10), fieldName, newFieldName, false);
|
||||
Processor processor = createRenameProcessor(fieldName, newFieldName, false);
|
||||
processor.execute(ingestDocument);
|
||||
assertThat(ingestDocument.getFieldValue(newFieldName, Object.class), equalTo(fieldValue));
|
||||
}
|
||||
|
@ -63,7 +64,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
document.put("one", one);
|
||||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
|
||||
|
||||
Processor processor = new RenameProcessor(randomAlphaOfLength(10), "list.0", "item", false);
|
||||
Processor processor = createRenameProcessor("list.0", "item", false);
|
||||
processor.execute(ingestDocument);
|
||||
Object actualObject = ingestDocument.getSourceAndMetadata().get("list");
|
||||
assertThat(actualObject, instanceOf(List.class));
|
||||
|
@ -76,7 +77,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
assertThat(actualObject, instanceOf(String.class));
|
||||
assertThat(actualObject, equalTo("item1"));
|
||||
|
||||
processor = new RenameProcessor(randomAlphaOfLength(10), "list.0", "list.3", false);
|
||||
processor = createRenameProcessor("list.0", "list.3", false);
|
||||
try {
|
||||
processor.execute(ingestDocument);
|
||||
fail("processor execute should have failed");
|
||||
|
@ -91,7 +92,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
public void testRenameNonExistingField() throws Exception {
|
||||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
|
||||
String fieldName = RandomDocumentPicks.randomFieldName(random());
|
||||
Processor processor = new RenameProcessor(randomAlphaOfLength(10), fieldName,
|
||||
Processor processor = createRenameProcessor(fieldName,
|
||||
RandomDocumentPicks.randomFieldName(random()), false);
|
||||
try {
|
||||
processor.execute(ingestDocument);
|
||||
|
@ -105,7 +106,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
|
||||
IngestDocument ingestDocument = new IngestDocument(originalIngestDocument);
|
||||
String fieldName = RandomDocumentPicks.randomFieldName(random());
|
||||
Processor processor = new RenameProcessor(randomAlphaOfLength(10), fieldName,
|
||||
Processor processor = createRenameProcessor(fieldName,
|
||||
RandomDocumentPicks.randomFieldName(random()), true);
|
||||
processor.execute(ingestDocument);
|
||||
assertIngestDocument(originalIngestDocument, ingestDocument);
|
||||
|
@ -114,7 +115,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
public void testRenameNewFieldAlreadyExists() throws Exception {
|
||||
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
|
||||
String fieldName = RandomDocumentPicks.randomExistingFieldName(random(), ingestDocument);
|
||||
Processor processor = new RenameProcessor(randomAlphaOfLength(10), RandomDocumentPicks.randomExistingFieldName(
|
||||
Processor processor = createRenameProcessor(RandomDocumentPicks.randomExistingFieldName(
|
||||
random(), ingestDocument), fieldName, false);
|
||||
try {
|
||||
processor.execute(ingestDocument);
|
||||
|
@ -129,7 +130,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
String fieldName = RandomDocumentPicks.randomFieldName(random());
|
||||
ingestDocument.setFieldValue(fieldName, null);
|
||||
String newFieldName = randomValueOtherThanMany(ingestDocument::hasField, () -> RandomDocumentPicks.randomFieldName(random()));
|
||||
Processor processor = new RenameProcessor(randomAlphaOfLength(10), fieldName, newFieldName, false);
|
||||
Processor processor = createRenameProcessor(fieldName, newFieldName, false);
|
||||
processor.execute(ingestDocument);
|
||||
assertThat(ingestDocument.hasField(fieldName), equalTo(false));
|
||||
assertThat(ingestDocument.hasField(newFieldName), equalTo(true));
|
||||
|
@ -149,7 +150,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
source.put("list", Collections.singletonList("item"));
|
||||
|
||||
IngestDocument ingestDocument = new IngestDocument(source, Collections.emptyMap());
|
||||
Processor processor = new RenameProcessor(randomAlphaOfLength(10), "list", "new_field", false);
|
||||
Processor processor = createRenameProcessor("list", "new_field", false);
|
||||
try {
|
||||
processor.execute(ingestDocument);
|
||||
fail("processor execute should have failed");
|
||||
|
@ -173,7 +174,7 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
source.put("list", Collections.singletonList("item"));
|
||||
|
||||
IngestDocument ingestDocument = new IngestDocument(source, Collections.emptyMap());
|
||||
Processor processor = new RenameProcessor(randomAlphaOfLength(10), "list", "new_field", false);
|
||||
Processor processor = createRenameProcessor("list", "new_field", false);
|
||||
try {
|
||||
processor.execute(ingestDocument);
|
||||
fail("processor execute should have failed");
|
||||
|
@ -188,12 +189,12 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
Map<String, Object> source = new HashMap<>();
|
||||
source.put("foo", "bar");
|
||||
IngestDocument ingestDocument = new IngestDocument(source, Collections.emptyMap());
|
||||
Processor processor1 = new RenameProcessor(randomAlphaOfLength(10), "foo", "foo.bar", false);
|
||||
Processor processor1 = createRenameProcessor("foo", "foo.bar", false);
|
||||
processor1.execute(ingestDocument);
|
||||
assertThat(ingestDocument.getFieldValue("foo", Map.class), equalTo(Collections.singletonMap("bar", "bar")));
|
||||
assertThat(ingestDocument.getFieldValue("foo.bar", String.class), equalTo("bar"));
|
||||
|
||||
Processor processor2 = new RenameProcessor(randomAlphaOfLength(10), "foo.bar", "foo.bar.baz", false);
|
||||
Processor processor2 = createRenameProcessor( "foo.bar", "foo.bar.baz", false);
|
||||
processor2.execute(ingestDocument);
|
||||
assertThat(ingestDocument.getFieldValue("foo", Map.class), equalTo(Collections.singletonMap("bar",
|
||||
Collections.singletonMap("baz", "bar"))));
|
||||
|
@ -201,9 +202,13 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
assertThat(ingestDocument.getFieldValue("foo.bar.baz", String.class), equalTo("bar"));
|
||||
|
||||
// for fun lets try to restore it (which don't allow today)
|
||||
Processor processor3 = new RenameProcessor(randomAlphaOfLength(10), "foo.bar.baz", "foo", false);
|
||||
Processor processor3 = createRenameProcessor("foo.bar.baz", "foo", false);
|
||||
Exception e = expectThrows(IllegalArgumentException.class, () -> processor3.execute(ingestDocument));
|
||||
assertThat(e.getMessage(), equalTo("field [foo] already exists"));
|
||||
}
|
||||
|
||||
private RenameProcessor createRenameProcessor(String field, String targetField, boolean ignoreMissing) {
|
||||
return new RenameProcessor(randomAlphaOfLength(10), new TestTemplateService.MockTemplateScript.Factory(field),
|
||||
new TestTemplateService.MockTemplateScript.Factory(targetField), ignoreMissing);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import org.apache.tools.ant.types.Path
|
||||
|
||||
|
||||
esplugin {
|
||||
description 'An easy, safe and fast scripting language for Elasticsearch'
|
||||
|
|
|
@ -24,13 +24,14 @@ import java.util.List;
|
|||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Whitelist contains data structures designed to be used to generate a white-list of Java classes,
|
||||
* Whitelist contains data structures designed to be used to generate a whitelist of Java classes,
|
||||
* constructors, methods, and fields that can be used within a Painless script at both compile-time
|
||||
* and run-time.
|
||||
*
|
||||
* A white-list consists of several pieces with {@link Struct}s as the top level. Each {@link Struct}
|
||||
* will contain zero-to-many {@link Constructor}s, {@link Method}s, and {@link Field}s which are what
|
||||
* will be available with a Painless script. See each individual white-list object for more detail.
|
||||
* A whitelist consists of several pieces with {@link WhitelistClass}s as the top level. Each
|
||||
* {@link WhitelistClass} will contain zero-to-many {@link WhitelistConstructor}s, {@link WhitelistMethod}s, and
|
||||
* {@link WhitelistField}s which are what will be available with a Painless script. See each individual
|
||||
* whitelist object for more detail.
|
||||
*/
|
||||
public final class Whitelist {
|
||||
|
||||
|
@ -54,166 +55,14 @@ public final class Whitelist {
|
|||
public static final List<Whitelist> BASE_WHITELISTS =
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Whitelist.class, BASE_WHITELIST_FILES));
|
||||
|
||||
/**
|
||||
* Struct represents the equivalent of a Java class in Painless complete with super classes,
|
||||
* constructors, methods, and fields. In Painless a class is known as a struct primarily to avoid
|
||||
* naming conflicts internally. There must be a one-to-one mapping of struct names to Java classes.
|
||||
* Though, since multiple white-lists may be combined into a single white-list for a specific
|
||||
* {@link org.elasticsearch.script.ScriptContext}, as long as multiple structs representing the same
|
||||
* Java class have the same Painless type name and have legal constructor/method overloading they
|
||||
* can be merged together.
|
||||
*
|
||||
* Structs in Painless allow for arity overloading for constructors and methods. Arity overloading
|
||||
* means that multiple constructors are allowed for a single struct as long as they have a different
|
||||
* number of parameter types, and multiples methods with the same name are allowed for a single struct
|
||||
* as long as they have the same return type and a different number of parameter types.
|
||||
*
|
||||
* Structs will automatically extend other white-listed structs if the Java class they represent is a
|
||||
* subclass of other structs including Java interfaces.
|
||||
*/
|
||||
public static final class Struct {
|
||||
|
||||
/** Information about where this struct was white-listed from. Can be used for error messages. */
|
||||
public final String origin;
|
||||
|
||||
/** The Java class name this struct represents. */
|
||||
public final String javaClassName;
|
||||
|
||||
/**
|
||||
* Allow the Java class name to only be specified as the fully-qualified name.
|
||||
*/
|
||||
public final boolean onlyFQNJavaClassName;
|
||||
|
||||
/** The {@link List} of white-listed ({@link Constructor}s) available to this struct. */
|
||||
public final List<Constructor> whitelistConstructors;
|
||||
|
||||
/** The {@link List} of white-listed ({@link Method}s) available to this struct. */
|
||||
public final List<Method> whitelistMethods;
|
||||
|
||||
/** The {@link List} of white-listed ({@link Field}s) available to this struct. */
|
||||
public final List<Field> whitelistFields;
|
||||
|
||||
/** Standard constructor. All values must be not {@code null}. */
|
||||
public Struct(String origin, String javaClassName, boolean onlyFQNJavaClassName,
|
||||
List<Constructor> whitelistConstructors, List<Method> whitelistMethods, List<Field> whitelistFields) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
this.javaClassName = Objects.requireNonNull(javaClassName);
|
||||
this.onlyFQNJavaClassName = onlyFQNJavaClassName;
|
||||
|
||||
this.whitelistConstructors = Collections.unmodifiableList(Objects.requireNonNull(whitelistConstructors));
|
||||
this.whitelistMethods = Collections.unmodifiableList(Objects.requireNonNull(whitelistMethods));
|
||||
this.whitelistFields = Collections.unmodifiableList(Objects.requireNonNull(whitelistFields));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor represents the equivalent of a Java constructor available as a white-listed struct
|
||||
* constructor within Painless. Constructors for Painless structs may be accessed exactly as
|
||||
* constructors for Java classes are using the 'new' keyword. Painless structs may have multiple
|
||||
* constructors as long as they comply with arity overloading described for {@link Struct}.
|
||||
*/
|
||||
public static final class Constructor {
|
||||
|
||||
/** Information about where this constructor was white-listed from. Can be used for error messages. */
|
||||
public final String origin;
|
||||
|
||||
/**
|
||||
* A {@link List} of {@link String}s that are the Painless type names for the parameters of the
|
||||
* constructor which can be used to look up the Java constructor through reflection.
|
||||
*/
|
||||
public final List<String> painlessParameterTypeNames;
|
||||
|
||||
/** Standard constructor. All values must be not {@code null}. */
|
||||
public Constructor(String origin, List<String> painlessParameterTypeNames) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method represents the equivalent of a Java method available as a white-listed struct method
|
||||
* within Painless. Methods for Painless structs may be accessed exactly as methods for Java classes
|
||||
* are using the '.' operator on an existing struct variable/field. Painless structs may have multiple
|
||||
* methods with the same name as long as they comply with arity overloading described for {@link Method}.
|
||||
*
|
||||
* Structs may also have additional methods that are not part of the Java class the struct represents -
|
||||
* these are known as augmented methods. An augmented method can be added to a struct as a part of any
|
||||
* Java class as long as the method is static and the first parameter of the method is the Java class
|
||||
* represented by the struct. Note that the augmented method's parent Java class does not need to be
|
||||
* white-listed.
|
||||
*/
|
||||
public static class Method {
|
||||
|
||||
/** Information about where this method was white-listed from. Can be used for error messages. */
|
||||
public final String origin;
|
||||
|
||||
/**
|
||||
* The Java class name for the owner of an augmented method. If the method is not augmented
|
||||
* this should be {@code null}.
|
||||
*/
|
||||
public final String javaAugmentedClassName;
|
||||
|
||||
/** The Java method name used to look up the Java method through reflection. */
|
||||
public final String javaMethodName;
|
||||
|
||||
/**
|
||||
* The Painless type name for the return type of the method which can be used to look up the Java
|
||||
* method through reflection.
|
||||
*/
|
||||
public final String painlessReturnTypeName;
|
||||
|
||||
/**
|
||||
* A {@link List} of {@link String}s that are the Painless type names for the parameters of the
|
||||
* method which can be used to look up the Java method through reflection.
|
||||
*/
|
||||
public final List<String> painlessParameterTypeNames;
|
||||
|
||||
/**
|
||||
* Standard constructor. All values must be not {@code null} with the exception of jAugmentedClass;
|
||||
* jAugmentedClass will be {@code null} unless the method is augmented as described in the class documentation.
|
||||
*/
|
||||
public Method(String origin, String javaAugmentedClassName, String javaMethodName,
|
||||
String painlessReturnTypeName, List<String> painlessParameterTypeNames) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
this.javaAugmentedClassName = javaAugmentedClassName;
|
||||
this.javaMethodName = javaMethodName;
|
||||
this.painlessReturnTypeName = Objects.requireNonNull(painlessReturnTypeName);
|
||||
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Field represents the equivalent of a Java field available as a white-listed struct field
|
||||
* within Painless. Fields for Painless structs may be accessed exactly as fields for Java classes
|
||||
* are using the '.' operator on an existing struct variable/field.
|
||||
*/
|
||||
public static class Field {
|
||||
|
||||
/** Information about where this method was white-listed from. Can be used for error messages. */
|
||||
public final String origin;
|
||||
|
||||
/** The Java field name used to look up the Java field through reflection. */
|
||||
public final String javaFieldName;
|
||||
|
||||
/** The Painless type name for the field which can be used to look up the Java field through reflection. */
|
||||
public final String painlessFieldTypeName;
|
||||
|
||||
/** Standard constructor. All values must be not {@code null}. */
|
||||
public Field(String origin, String javaFieldName, String painlessFieldTypeName) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
this.javaFieldName = Objects.requireNonNull(javaFieldName);
|
||||
this.painlessFieldTypeName = Objects.requireNonNull(painlessFieldTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
/** The {@link ClassLoader} used to look up the white-listed Java classes, constructors, methods, and fields. */
|
||||
/** The {@link ClassLoader} used to look up the whitelisted Java classes, constructors, methods, and fields. */
|
||||
public final ClassLoader javaClassLoader;
|
||||
|
||||
/** The {@link List} of all the white-listed Painless structs. */
|
||||
public final List<Struct> whitelistStructs;
|
||||
/** The {@link List} of all the whitelisted Painless classes. */
|
||||
public final List<WhitelistClass> whitelistStructs;
|
||||
|
||||
/** Standard constructor. All values must be not {@code null}. */
|
||||
public Whitelist(ClassLoader javaClassLoader, List<Struct> whitelistStructs) {
|
||||
public Whitelist(ClassLoader javaClassLoader, List<WhitelistClass> whitelistStructs) {
|
||||
this.javaClassLoader = Objects.requireNonNull(javaClassLoader);
|
||||
this.whitelistStructs = Collections.unmodifiableList(Objects.requireNonNull(whitelistStructs));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.spi;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Class represents the equivalent of a Java class in Painless complete with super classes,
|
||||
* constructors, methods, and fields. There must be a one-to-one mapping of class names to Java
|
||||
* classes. Though, since multiple whitelists may be combined into a single whitelist for a
|
||||
* specific context, as long as multiple classes representing the same Java class have the same
|
||||
* class name and have legal constructor/method overloading they can be merged together.
|
||||
*
|
||||
* Classes in Painless allow for arity overloading for constructors and methods. Arity overloading
|
||||
* means that multiple constructors are allowed for a single class as long as they have a different
|
||||
* number of parameters, and multiples methods with the same name are allowed for a single class
|
||||
* as long as they have the same return type and a different number of parameters.
|
||||
*
|
||||
* Classes will automatically extend other whitelisted classes if the Java class they represent is a
|
||||
* subclass of other classes including Java interfaces.
|
||||
*/
|
||||
public final class WhitelistClass {
|
||||
|
||||
/** Information about where this class was white-listed from. Can be used for error messages. */
|
||||
public final String origin;
|
||||
|
||||
/** The Java class name this class represents. */
|
||||
public final String javaClassName;
|
||||
|
||||
/**
|
||||
* Allow the Java class name to only be specified as the fully-qualified name.
|
||||
*/
|
||||
public final boolean onlyFQNJavaClassName;
|
||||
|
||||
/** The {@link List} of whitelisted ({@link WhitelistConstructor}s) available to this class. */
|
||||
public final List<WhitelistConstructor> whitelistConstructors;
|
||||
|
||||
/** The {@link List} of whitelisted ({@link WhitelistMethod}s) available to this class. */
|
||||
public final List<WhitelistMethod> whitelistMethods;
|
||||
|
||||
/** The {@link List} of whitelisted ({@link WhitelistField}s) available to this class. */
|
||||
public final List<WhitelistField> whitelistFields;
|
||||
|
||||
/** Standard constructor. All values must be not {@code null}. */
|
||||
public WhitelistClass(String origin, String javaClassName, boolean onlyFQNJavaClassName,
|
||||
List<WhitelistConstructor> whitelistConstructors,
|
||||
List<WhitelistMethod> whitelistMethods,
|
||||
List<WhitelistField> whitelistFields) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
this.javaClassName = Objects.requireNonNull(javaClassName);
|
||||
this.onlyFQNJavaClassName = onlyFQNJavaClassName;
|
||||
|
||||
this.whitelistConstructors = Collections.unmodifiableList(Objects.requireNonNull(whitelistConstructors));
|
||||
this.whitelistMethods = Collections.unmodifiableList(Objects.requireNonNull(whitelistMethods));
|
||||
this.whitelistFields = Collections.unmodifiableList(Objects.requireNonNull(whitelistFields));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.spi;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Constructor represents the equivalent of a Java constructor available as a whitelisted class
|
||||
* constructor within Painless. Constructors for Painless classes may be accessed exactly as
|
||||
* constructors for Java classes are using the 'new' keyword. Painless classes may have multiple
|
||||
* constructors as long as they comply with arity overloading described for {@link WhitelistClass}.
|
||||
*/
|
||||
public final class WhitelistConstructor {
|
||||
|
||||
/** Information about where this constructor was whitelisted from. Can be used for error messages. */
|
||||
public final String origin;
|
||||
|
||||
/**
|
||||
* A {@link List} of {@link String}s that are the Painless type names for the parameters of the
|
||||
* constructor which can be used to look up the Java constructor through reflection.
|
||||
*/
|
||||
public final List<String> painlessParameterTypeNames;
|
||||
|
||||
/** Standard constructor. All values must be not {@code null}. */
|
||||
public WhitelistConstructor(String origin, List<String> painlessParameterTypeNames) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.spi;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Field represents the equivalent of a Java field available as a whitelisted class field
|
||||
* within Painless. Fields for Painless classes may be accessed exactly as fields for Java classes
|
||||
* are using the '.' operator on an existing class variable/field.
|
||||
*/
|
||||
public class WhitelistField {
|
||||
|
||||
/** Information about where this method was whitelisted from. Can be used for error messages. */
|
||||
public final String origin;
|
||||
|
||||
/** The Java field name used to look up the Java field through reflection. */
|
||||
public final String javaFieldName;
|
||||
|
||||
/** The Painless type name for the field which can be used to look up the Java field through reflection. */
|
||||
public final String painlessFieldTypeName;
|
||||
|
||||
/** Standard constructor. All values must be not {@code null}. */
|
||||
public WhitelistField(String origin, String javaFieldName, String painlessFieldTypeName) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
this.javaFieldName = Objects.requireNonNull(javaFieldName);
|
||||
this.painlessFieldTypeName = Objects.requireNonNull(painlessFieldTypeName);
|
||||
}
|
||||
}
|
|
@ -39,25 +39,25 @@ public final class WhitelistLoader {
|
|||
* {@link String}s with a single {@link Class} to be be used to load the resources where each {@link String}
|
||||
* is the path of a single text file. The {@link Class}'s {@link ClassLoader} will be used to lookup the Java
|
||||
* reflection objects for each individual {@link Class}, {@link Constructor}, {@link Method}, and {@link Field}
|
||||
* specified as part of the white-list in the text file.
|
||||
* specified as part of the whitelist in the text file.
|
||||
*
|
||||
* A single pass is made through each file to collect all the information about each struct, constructor, method,
|
||||
* and field. Most validation will be done at a later point after all white-lists have been gathered and their
|
||||
* A single pass is made through each file to collect all the information about each class, constructor, method,
|
||||
* and field. Most validation will be done at a later point after all whitelists have been gathered and their
|
||||
* merging takes place.
|
||||
*
|
||||
* A painless type name is one of the following:
|
||||
* <ul>
|
||||
* <li> def - The Painless dynamic type which is automatically included without a need to be
|
||||
* white-listed. </li>
|
||||
* <li> fully-qualified Java type name - Any white-listed Java class will have the equivalent name as
|
||||
* whitelisted. </li>
|
||||
* <li> fully-qualified Java type name - Any whitelisted Java class will have the equivalent name as
|
||||
* a Painless type name with the exception that any dollar symbols used as part of inner classes will
|
||||
* be replaced with dot symbols. </li>
|
||||
* <li> short Java type name - The text after the final dot symbol of any specified Java class. A
|
||||
* short type Java name may be excluded by using the 'only_fqn' token during Painless struct parsing
|
||||
* short type Java name may be excluded by using the 'only_fqn' token during Painless class parsing
|
||||
* as described later. </li>
|
||||
* </ul>
|
||||
*
|
||||
* The following can be parsed from each white-list text file:
|
||||
* The following can be parsed from each whitelist text file:
|
||||
* <ul>
|
||||
* <li> Blank lines will be ignored by the parser. </li>
|
||||
* <li> Comments may be created starting with a pound '#' symbol and end with a newline. These will
|
||||
|
@ -71,19 +71,19 @@ public final class WhitelistLoader {
|
|||
* <ul>
|
||||
* <li> A constructor may be specified starting with an opening parenthesis, followed by a
|
||||
* comma-delimited list of Painless type names corresponding to the type/class names for
|
||||
* the equivalent Java parameter types (these must be white-listed as well), a closing
|
||||
* the equivalent Java parameter types (these must be whitelisted as well), a closing
|
||||
* parenthesis, and a newline. </li>
|
||||
* <li> A method may be specified starting with a Painless type name for the return type,
|
||||
* followed by the Java name of the method (which will also be the Painless name for the
|
||||
* method), an opening parenthesis, a comma-delimited list of Painless type names
|
||||
* corresponding to the type/class names for the equivalent Java parameter types
|
||||
* (these must be white-listed as well), a closing parenthesis, and a newline. </li>
|
||||
* (these must be whitelisted as well), a closing parenthesis, and a newline. </li>
|
||||
* <li> An augmented method may be specified starting with a Painless type name for the return
|
||||
* type, followed by the fully qualified Java name of the class the augmented method is
|
||||
* part of (this class does not need to be white-listed), the Java name of the method
|
||||
* part of (this class does not need to be whitelisted), the Java name of the method
|
||||
* (which will also be the Painless name for the method), an opening parenthesis, a
|
||||
* comma-delimited list of Painless type names corresponding to the type/class names
|
||||
* for the equivalent Java parameter types (these must be white-listed as well), a closing
|
||||
* for the equivalent Java parameter types (these must be whitelisted as well), a closing
|
||||
* parenthesis, and a newline. </li>
|
||||
* <li>A field may be specified starting with a Painless type name for the equivalent Java type
|
||||
* of the field, followed by the Java name of the field (which all be the Painless name
|
||||
|
@ -99,7 +99,7 @@ public final class WhitelistLoader {
|
|||
* fully-qualified Java class name. Method argument types, method return types, and field types
|
||||
* must be specified with Painless type names (def, fully-qualified, or short) as described earlier.
|
||||
*
|
||||
* The following example is used to create a single white-list text file:
|
||||
* The following example is used to create a single whitelist text file:
|
||||
*
|
||||
* {@code
|
||||
* # primitive types
|
||||
|
@ -132,10 +132,10 @@ public final class WhitelistLoader {
|
|||
* }
|
||||
*/
|
||||
public static Whitelist loadFromResourceFiles(Class<?> resource, String... filepaths) {
|
||||
List<Whitelist.Struct> whitelistStructs = new ArrayList<>();
|
||||
List<WhitelistClass> whitelistStructs = new ArrayList<>();
|
||||
|
||||
// Execute a single pass through the white-list text files. This will gather all the
|
||||
// constructors, methods, augmented methods, and fields for each white-listed struct.
|
||||
// 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;
|
||||
|
@ -146,9 +146,9 @@ public final class WhitelistLoader {
|
|||
String whitelistStructOrigin = null;
|
||||
String javaClassName = null;
|
||||
boolean onlyFQNJavaClassName = false;
|
||||
List<Whitelist.Constructor> whitelistConstructors = null;
|
||||
List<Whitelist.Method> whitelistMethods = null;
|
||||
List<Whitelist.Field> whitelistFields = null;
|
||||
List<WhitelistConstructor> whitelistConstructors = null;
|
||||
List<WhitelistMethod> whitelistMethods = null;
|
||||
List<WhitelistField> whitelistFields = null;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
number = reader.getLineNumber();
|
||||
|
@ -159,13 +159,13 @@ public final class WhitelistLoader {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Handle a new struct by resetting all the variables necessary to construct a new Whitelist.Struct for the white-list.
|
||||
// Handle a new class by resetting all the variables necessary to construct a new WhitelistClass for the whitelist.
|
||||
// Expects the following format: 'class' ID 'only_fqn'? '{' '\n'
|
||||
if (line.startsWith("class ")) {
|
||||
// Ensure the final token of the line is '{'.
|
||||
if (line.endsWith("{") == false) {
|
||||
throw new IllegalArgumentException(
|
||||
"invalid struct definition: failed to parse class opening bracket [" + line + "]");
|
||||
"invalid class definition: failed to parse class opening bracket [" + line + "]");
|
||||
}
|
||||
|
||||
// Parse the Java class name.
|
||||
|
@ -175,29 +175,29 @@ public final class WhitelistLoader {
|
|||
if (tokens.length == 2 && "only_fqn".equals(tokens[1])) {
|
||||
onlyFQNJavaClassName = true;
|
||||
} else if (tokens.length != 1) {
|
||||
throw new IllegalArgumentException("invalid struct definition: failed to parse class name [" + line + "]");
|
||||
throw new IllegalArgumentException("invalid class definition: failed to parse class name [" + line + "]");
|
||||
}
|
||||
|
||||
whitelistStructOrigin = "[" + filepath + "]:[" + number + "]";
|
||||
javaClassName = tokens[0];
|
||||
|
||||
// Reset all the constructors, methods, and fields to support a new struct.
|
||||
// Reset all the constructors, methods, and fields to support a new class.
|
||||
whitelistConstructors = new ArrayList<>();
|
||||
whitelistMethods = new ArrayList<>();
|
||||
whitelistFields = new ArrayList<>();
|
||||
|
||||
// Handle the end of a struct, by creating a new Whitelist.Struct with all the previously gathered
|
||||
// constructors, methods, augmented methods, and fields, and adding it to the list of white-listed structs.
|
||||
// Handle the end of a class, by creating a new WhitelistClass with all the previously gathered
|
||||
// constructors, methods, augmented methods, and fields, and adding it to the list of whitelisted classes.
|
||||
// Expects the following format: '}' '\n'
|
||||
} else if (line.equals("}")) {
|
||||
if (javaClassName == null) {
|
||||
throw new IllegalArgumentException("invalid struct definition: extraneous closing bracket");
|
||||
throw new IllegalArgumentException("invalid class definition: extraneous closing bracket");
|
||||
}
|
||||
|
||||
whitelistStructs.add(new Whitelist.Struct(whitelistStructOrigin, javaClassName, onlyFQNJavaClassName,
|
||||
whitelistStructs.add(new WhitelistClass(whitelistStructOrigin, javaClassName, onlyFQNJavaClassName,
|
||||
whitelistConstructors, whitelistMethods, whitelistFields));
|
||||
|
||||
// Set all the variables to null to ensure a new struct definition is found before other parsable values.
|
||||
// Set all the variables to null to ensure a new class definition is found before other parsable values.
|
||||
whitelistStructOrigin = null;
|
||||
javaClassName = null;
|
||||
onlyFQNJavaClassName = false;
|
||||
|
@ -210,7 +210,7 @@ public final class WhitelistLoader {
|
|||
// Mark the origin of this parsable object.
|
||||
String origin = "[" + filepath + "]:[" + number + "]";
|
||||
|
||||
// Ensure we have a defined struct before adding any constructors, methods, augmented methods, or fields.
|
||||
// Ensure we have a defined class before adding any constructors, methods, augmented methods, or fields.
|
||||
if (javaClassName == null) {
|
||||
throw new IllegalArgumentException("invalid object definition: expected a class name [" + line + "]");
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ public final class WhitelistLoader {
|
|||
tokens = new String[0];
|
||||
}
|
||||
|
||||
whitelistConstructors.add(new Whitelist.Constructor(origin, Arrays.asList(tokens)));
|
||||
whitelistConstructors.add(new WhitelistConstructor(origin, Arrays.asList(tokens)));
|
||||
|
||||
// Handle the case for a method or augmented method definition.
|
||||
// Expects the following format: ID ID? ID '(' ( ID ( ',' ID )* )? ')' '\n'
|
||||
|
@ -271,7 +271,7 @@ public final class WhitelistLoader {
|
|||
tokens = new String[0];
|
||||
}
|
||||
|
||||
whitelistMethods.add(new Whitelist.Method(origin, javaAugmentedClassName, javaMethodName,
|
||||
whitelistMethods.add(new WhitelistMethod(origin, javaAugmentedClassName, javaMethodName,
|
||||
painlessReturnTypeName, Arrays.asList(tokens)));
|
||||
|
||||
// Handle the case for a field definition.
|
||||
|
@ -285,14 +285,14 @@ public final class WhitelistLoader {
|
|||
throw new IllegalArgumentException("invalid field definition: unexpected format [" + line + "]");
|
||||
}
|
||||
|
||||
whitelistFields.add(new Whitelist.Field(origin, tokens[1], tokens[0]));
|
||||
whitelistFields.add(new WhitelistField(origin, tokens[1], tokens[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure all structs end with a '}' token before the end of the file.
|
||||
// Ensure all classes end with a '}' token before the end of the file.
|
||||
if (javaClassName != null) {
|
||||
throw new IllegalArgumentException("invalid struct definition: expected closing bracket");
|
||||
throw new IllegalArgumentException("invalid class definition: expected closing bracket");
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException("error in [" + filepath + "] at line [" + number + "]", exception);
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.spi;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Method represents the equivalent of a Java method available as a whitelisted class method
|
||||
* within Painless. Methods for Painless classes may be accessed exactly as methods for Java classes
|
||||
* are using the '.' operator on an existing class variable/field. Painless classes may have multiple
|
||||
* methods with the same name as long as they comply with arity overloading described for {@link WhitelistMethod}.
|
||||
*
|
||||
* Classes may also have additional methods that are not part of the Java class the class represents -
|
||||
* these are known as augmented methods. An augmented method can be added to a class as a part of any
|
||||
* Java class as long as the method is static and the first parameter of the method is the Java class
|
||||
* represented by the class. Note that the augmented method's parent Java class does not need to be
|
||||
* whitelisted.
|
||||
*/
|
||||
public class WhitelistMethod {
|
||||
|
||||
/** Information about where this method was whitelisted from. Can be used for error messages. */
|
||||
public final String origin;
|
||||
|
||||
/**
|
||||
* The Java class name for the owner of an augmented method. If the method is not augmented
|
||||
* this should be {@code null}.
|
||||
*/
|
||||
public final String javaAugmentedClassName;
|
||||
|
||||
/** The Java method name used to look up the Java method through reflection. */
|
||||
public final String javaMethodName;
|
||||
|
||||
/**
|
||||
* The Painless type name for the return type of the method which can be used to look up the Java
|
||||
* method through reflection.
|
||||
*/
|
||||
public final String painlessReturnTypeName;
|
||||
|
||||
/**
|
||||
* A {@link List} of {@link String}s that are the Painless type names for the parameters of the
|
||||
* method which can be used to look up the Java method through reflection.
|
||||
*/
|
||||
public final List<String> painlessParameterTypeNames;
|
||||
|
||||
/**
|
||||
* Standard constructor. All values must be not {@code null} with the exception of jAugmentedClass;
|
||||
* jAugmentedClass will be {@code null} unless the method is augmented as described in the class documentation.
|
||||
*/
|
||||
public WhitelistMethod(String origin, String javaAugmentedClassName, String javaMethodName,
|
||||
String painlessReturnTypeName, List<String> painlessParameterTypeNames) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
this.javaAugmentedClassName = javaAugmentedClassName;
|
||||
this.javaMethodName = javaMethodName;
|
||||
this.painlessReturnTypeName = Objects.requireNonNull(painlessReturnTypeName);
|
||||
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
|
||||
}
|
||||
}
|
|
@ -19,8 +19,9 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessCast;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -30,7 +31,7 @@ import java.util.Objects;
|
|||
*/
|
||||
public final class AnalyzerCaster {
|
||||
|
||||
public static Cast getLegalCast(Location location, Class<?> actual, Class<?> expected, boolean explicit, boolean internal) {
|
||||
public static PainlessCast getLegalCast(Location location, Class<?> actual, Class<?> expected, boolean explicit, boolean internal) {
|
||||
Objects.requireNonNull(actual);
|
||||
Objects.requireNonNull(expected);
|
||||
|
||||
|
@ -40,421 +41,421 @@ public final class AnalyzerCaster {
|
|||
|
||||
if (actual == def.class) {
|
||||
if (expected == boolean.class) {
|
||||
return Cast.unboxTo(def.class, Boolean.class, explicit, boolean.class);
|
||||
return PainlessCast.unboxTo(def.class, Boolean.class, explicit, boolean.class);
|
||||
} else if (expected == byte.class) {
|
||||
return Cast.unboxTo(def.class, Byte.class, explicit, byte.class);
|
||||
return PainlessCast.unboxTo(def.class, Byte.class, explicit, byte.class);
|
||||
} else if (expected == short.class) {
|
||||
return Cast.unboxTo(def.class, Short.class, explicit, short.class);
|
||||
return PainlessCast.unboxTo(def.class, Short.class, explicit, short.class);
|
||||
} else if (expected == char.class) {
|
||||
return Cast.unboxTo(def.class, Character.class, explicit, char.class);
|
||||
return PainlessCast.unboxTo(def.class, Character.class, explicit, char.class);
|
||||
} else if (expected == int.class) {
|
||||
return Cast.unboxTo(def.class, Integer.class, explicit, int.class);
|
||||
return PainlessCast.unboxTo(def.class, Integer.class, explicit, int.class);
|
||||
} else if (expected == long.class) {
|
||||
return Cast.unboxTo(def.class, Long.class, explicit, long.class);
|
||||
return PainlessCast.unboxTo(def.class, Long.class, explicit, long.class);
|
||||
} else if (expected == float.class) {
|
||||
return Cast.unboxTo(def.class, Float.class, explicit, float.class);
|
||||
return PainlessCast.unboxTo(def.class, Float.class, explicit, float.class);
|
||||
} else if (expected == double.class) {
|
||||
return Cast.unboxTo(def.class, Double.class, explicit, double.class);
|
||||
return PainlessCast.unboxTo(def.class, Double.class, explicit, double.class);
|
||||
}
|
||||
} else if (actual == Object.class) {
|
||||
if (expected == byte.class && explicit && internal) {
|
||||
return Cast.unboxTo(Object.class, Byte.class, true, byte.class);
|
||||
return PainlessCast.unboxTo(Object.class, Byte.class, true, byte.class);
|
||||
} else if (expected == short.class && explicit && internal) {
|
||||
return Cast.unboxTo(Object.class, Short.class, true, short.class);
|
||||
return PainlessCast.unboxTo(Object.class, Short.class, true, short.class);
|
||||
} else if (expected == char.class && explicit && internal) {
|
||||
return Cast.unboxTo(Object.class, Character.class, true, char.class);
|
||||
return PainlessCast.unboxTo(Object.class, Character.class, true, char.class);
|
||||
} else if (expected == int.class && explicit && internal) {
|
||||
return Cast.unboxTo(Object.class, Integer.class, true, int.class);
|
||||
return PainlessCast.unboxTo(Object.class, Integer.class, true, int.class);
|
||||
} else if (expected == long.class && explicit && internal) {
|
||||
return Cast.unboxTo(Object.class, Long.class, true, long.class);
|
||||
return PainlessCast.unboxTo(Object.class, Long.class, true, long.class);
|
||||
} else if (expected == float.class && explicit && internal) {
|
||||
return Cast.unboxTo(Object.class, Float.class, true, float.class);
|
||||
return PainlessCast.unboxTo(Object.class, Float.class, true, float.class);
|
||||
} else if (expected == double.class && explicit && internal) {
|
||||
return Cast.unboxTo(Object.class, Double.class, true, double.class);
|
||||
return PainlessCast.unboxTo(Object.class, Double.class, true, double.class);
|
||||
}
|
||||
} else if (actual == Number.class) {
|
||||
if (expected == byte.class && explicit && internal) {
|
||||
return Cast.unboxTo(Number.class, Byte.class, true, byte.class);
|
||||
return PainlessCast.unboxTo(Number.class, Byte.class, true, byte.class);
|
||||
} else if (expected == short.class && explicit && internal) {
|
||||
return Cast.unboxTo(Number.class, Short.class, true, short.class);
|
||||
return PainlessCast.unboxTo(Number.class, Short.class, true, short.class);
|
||||
} else if (expected == char.class && explicit && internal) {
|
||||
return Cast.unboxTo(Number.class, Character.class, true, char.class);
|
||||
return PainlessCast.unboxTo(Number.class, Character.class, true, char.class);
|
||||
} else if (expected == int.class && explicit && internal) {
|
||||
return Cast.unboxTo(Number.class, Integer.class, true, int.class);
|
||||
return PainlessCast.unboxTo(Number.class, Integer.class, true, int.class);
|
||||
} else if (expected == long.class && explicit && internal) {
|
||||
return Cast.unboxTo(Number.class, Long.class, true, long.class);
|
||||
return PainlessCast.unboxTo(Number.class, Long.class, true, long.class);
|
||||
} else if (expected == float.class && explicit && internal) {
|
||||
return Cast.unboxTo(Number.class, Float.class, true, float.class);
|
||||
return PainlessCast.unboxTo(Number.class, Float.class, true, float.class);
|
||||
} else if (expected == double.class && explicit && internal) {
|
||||
return Cast.unboxTo(Number.class, Double.class, true, double.class);
|
||||
return PainlessCast.unboxTo(Number.class, Double.class, true, double.class);
|
||||
}
|
||||
} else if (actual == String.class) {
|
||||
if (expected == char.class && explicit) {
|
||||
return Cast.standard(String.class, char.class, true);
|
||||
return PainlessCast.standard(String.class, char.class, true);
|
||||
}
|
||||
} else if (actual == boolean.class) {
|
||||
if (expected == def.class) {
|
||||
return Cast.boxFrom(Boolean.class, def.class, explicit, boolean.class);
|
||||
return PainlessCast.boxFrom(Boolean.class, def.class, explicit, boolean.class);
|
||||
} else if (expected == Object.class && internal) {
|
||||
return Cast.boxFrom(Boolean.class, Object.class, explicit, boolean.class);
|
||||
return PainlessCast.boxFrom(Boolean.class, Object.class, explicit, boolean.class);
|
||||
} else if (expected == Boolean.class && internal) {
|
||||
return Cast.boxTo(boolean.class, boolean.class, explicit, boolean.class);
|
||||
return PainlessCast.boxTo(boolean.class, boolean.class, explicit, boolean.class);
|
||||
}
|
||||
} else if (actual == byte.class) {
|
||||
if (expected == def.class) {
|
||||
return Cast.boxFrom(Byte.class, def.class, explicit, byte.class);
|
||||
return PainlessCast.boxFrom(Byte.class, def.class, explicit, byte.class);
|
||||
} else if (expected == Object.class && internal) {
|
||||
return Cast.boxFrom(Byte.class, Object.class, explicit, byte.class);
|
||||
return PainlessCast.boxFrom(Byte.class, Object.class, explicit, byte.class);
|
||||
} else if (expected == Number.class && internal) {
|
||||
return Cast.boxFrom(Byte.class, Number.class, explicit, byte.class);
|
||||
return PainlessCast.boxFrom(Byte.class, Number.class, explicit, byte.class);
|
||||
} else if (expected == short.class) {
|
||||
return Cast.standard(byte.class, short.class, explicit);
|
||||
return PainlessCast.standard(byte.class, short.class, explicit);
|
||||
} else if (expected == char.class && explicit) {
|
||||
return Cast.standard(byte.class, char.class, true);
|
||||
return PainlessCast.standard(byte.class, char.class, true);
|
||||
} else if (expected == int.class) {
|
||||
return Cast.standard(byte.class, int.class, explicit);
|
||||
return PainlessCast.standard(byte.class, int.class, explicit);
|
||||
} else if (expected == long.class) {
|
||||
return Cast.standard(byte.class, long.class, explicit);
|
||||
return PainlessCast.standard(byte.class, long.class, explicit);
|
||||
} else if (expected == float.class) {
|
||||
return Cast.standard(byte.class, float.class, explicit);
|
||||
return PainlessCast.standard(byte.class, float.class, explicit);
|
||||
} else if (expected == double.class) {
|
||||
return Cast.standard(byte.class, double.class, explicit);
|
||||
return PainlessCast.standard(byte.class, double.class, explicit);
|
||||
} else if (expected == Byte.class && internal) {
|
||||
return Cast.boxTo(byte.class, byte.class, explicit, byte.class);
|
||||
return PainlessCast.boxTo(byte.class, byte.class, explicit, byte.class);
|
||||
} else if (expected == Short.class && internal) {
|
||||
return Cast.boxTo(byte.class, short.class, explicit, short.class);
|
||||
return PainlessCast.boxTo(byte.class, short.class, explicit, short.class);
|
||||
} else if (expected == Character.class && explicit && internal) {
|
||||
return Cast.boxTo(byte.class, char.class, true, char.class);
|
||||
return PainlessCast.boxTo(byte.class, char.class, true, char.class);
|
||||
} else if (expected == Integer.class && internal) {
|
||||
return Cast.boxTo(byte.class, int.class, explicit, int.class);
|
||||
return PainlessCast.boxTo(byte.class, int.class, explicit, int.class);
|
||||
} else if (expected == Long.class && internal) {
|
||||
return Cast.boxTo(byte.class, long.class, explicit, long.class);
|
||||
return PainlessCast.boxTo(byte.class, long.class, explicit, long.class);
|
||||
} else if (expected == Float.class && internal) {
|
||||
return Cast.boxTo(byte.class, float.class, explicit, float.class);
|
||||
return PainlessCast.boxTo(byte.class, float.class, explicit, float.class);
|
||||
} else if (expected == Double.class && internal) {
|
||||
return Cast.boxTo(byte.class, double.class, explicit, double.class);
|
||||
return PainlessCast.boxTo(byte.class, double.class, explicit, double.class);
|
||||
}
|
||||
} else if (actual == short.class) {
|
||||
if (expected == def.class) {
|
||||
return Cast.boxFrom(Short.class, def.class, explicit, short.class);
|
||||
return PainlessCast.boxFrom(Short.class, def.class, explicit, short.class);
|
||||
} else if (expected == Object.class && internal) {
|
||||
return Cast.boxFrom(Short.class, Object.class, explicit, short.class);
|
||||
return PainlessCast.boxFrom(Short.class, Object.class, explicit, short.class);
|
||||
} else if (expected == Number.class && internal) {
|
||||
return Cast.boxFrom(Short.class, Number.class, explicit, short.class);
|
||||
return PainlessCast.boxFrom(Short.class, Number.class, explicit, short.class);
|
||||
} else if (expected == byte.class && explicit) {
|
||||
return Cast.standard(short.class, byte.class, true);
|
||||
return PainlessCast.standard(short.class, byte.class, true);
|
||||
} else if (expected == char.class && explicit) {
|
||||
return Cast.standard(short.class, char.class, true);
|
||||
return PainlessCast.standard(short.class, char.class, true);
|
||||
} else if (expected == int.class) {
|
||||
return Cast.standard(short.class, int.class, explicit);
|
||||
return PainlessCast.standard(short.class, int.class, explicit);
|
||||
} else if (expected == long.class) {
|
||||
return Cast.standard(short.class, long.class, explicit);
|
||||
return PainlessCast.standard(short.class, long.class, explicit);
|
||||
} else if (expected == float.class) {
|
||||
return Cast.standard(short.class, float.class, explicit);
|
||||
return PainlessCast.standard(short.class, float.class, explicit);
|
||||
} else if (expected == double.class) {
|
||||
return Cast.standard(short.class, double.class, explicit);
|
||||
return PainlessCast.standard(short.class, double.class, explicit);
|
||||
} else if (expected == Byte.class && explicit && internal) {
|
||||
return Cast.boxTo(short.class, byte.class, true, byte.class);
|
||||
return PainlessCast.boxTo(short.class, byte.class, true, byte.class);
|
||||
} else if (expected == Short.class && internal) {
|
||||
return Cast.boxTo(short.class, short.class, explicit, short.class);
|
||||
return PainlessCast.boxTo(short.class, short.class, explicit, short.class);
|
||||
} else if (expected == Character.class && explicit && internal) {
|
||||
return Cast.boxTo(short.class, char.class, true, char.class);
|
||||
return PainlessCast.boxTo(short.class, char.class, true, char.class);
|
||||
} else if (expected == Integer.class && internal) {
|
||||
return Cast.boxTo(short.class, int.class, explicit, int.class);
|
||||
return PainlessCast.boxTo(short.class, int.class, explicit, int.class);
|
||||
} else if (expected == Long.class && internal) {
|
||||
return Cast.boxTo(short.class, long.class, explicit, long.class);
|
||||
return PainlessCast.boxTo(short.class, long.class, explicit, long.class);
|
||||
} else if (expected == Float.class && internal) {
|
||||
return Cast.boxTo(short.class, float.class, explicit, float.class);
|
||||
return PainlessCast.boxTo(short.class, float.class, explicit, float.class);
|
||||
} else if (expected == Double.class && internal) {
|
||||
return Cast.boxTo(short.class, double.class, explicit, double.class);
|
||||
return PainlessCast.boxTo(short.class, double.class, explicit, double.class);
|
||||
}
|
||||
} else if (actual == char.class) {
|
||||
if (expected == def.class) {
|
||||
return Cast.boxFrom(Character.class, def.class, explicit, char.class);
|
||||
return PainlessCast.boxFrom(Character.class, def.class, explicit, char.class);
|
||||
} else if (expected == Object.class && internal) {
|
||||
return Cast.boxFrom(Character.class, Object.class, explicit, char.class);
|
||||
return PainlessCast.boxFrom(Character.class, Object.class, explicit, char.class);
|
||||
} else if (expected == Number.class && internal) {
|
||||
return Cast.boxFrom(Character.class, Number.class, explicit, char.class);
|
||||
return PainlessCast.boxFrom(Character.class, Number.class, explicit, char.class);
|
||||
} else if (expected == String.class) {
|
||||
return Cast.standard(char.class, String.class, explicit);
|
||||
return PainlessCast.standard(char.class, String.class, explicit);
|
||||
} else if (expected == byte.class && explicit) {
|
||||
return Cast.standard(char.class, byte.class, true);
|
||||
return PainlessCast.standard(char.class, byte.class, true);
|
||||
} else if (expected == short.class && explicit) {
|
||||
return Cast.standard(char.class, short.class, true);
|
||||
return PainlessCast.standard(char.class, short.class, true);
|
||||
} else if (expected == int.class) {
|
||||
return Cast.standard(char.class, int.class, explicit);
|
||||
return PainlessCast.standard(char.class, int.class, explicit);
|
||||
} else if (expected == long.class) {
|
||||
return Cast.standard(char.class, long.class, explicit);
|
||||
return PainlessCast.standard(char.class, long.class, explicit);
|
||||
} else if (expected == float.class) {
|
||||
return Cast.standard(char.class, float.class, explicit);
|
||||
return PainlessCast.standard(char.class, float.class, explicit);
|
||||
} else if (expected == double.class) {
|
||||
return Cast.standard(char.class, double.class, explicit);
|
||||
return PainlessCast.standard(char.class, double.class, explicit);
|
||||
} else if (expected == Byte.class && explicit && internal) {
|
||||
return Cast.boxTo(char.class, byte.class, true, byte.class);
|
||||
return PainlessCast.boxTo(char.class, byte.class, true, byte.class);
|
||||
} else if (expected == Short.class && internal) {
|
||||
return Cast.boxTo(char.class, short.class, explicit, short.class);
|
||||
return PainlessCast.boxTo(char.class, short.class, explicit, short.class);
|
||||
} else if (expected == Character.class && internal) {
|
||||
return Cast.boxTo(char.class, char.class, true, char.class);
|
||||
return PainlessCast.boxTo(char.class, char.class, true, char.class);
|
||||
} else if (expected == Integer.class && internal) {
|
||||
return Cast.boxTo(char.class, int.class, explicit, int.class);
|
||||
return PainlessCast.boxTo(char.class, int.class, explicit, int.class);
|
||||
} else if (expected == Long.class && internal) {
|
||||
return Cast.boxTo(char.class, long.class, explicit, long.class);
|
||||
return PainlessCast.boxTo(char.class, long.class, explicit, long.class);
|
||||
} else if (expected == Float.class && internal) {
|
||||
return Cast.boxTo(char.class, float.class, explicit, float.class);
|
||||
return PainlessCast.boxTo(char.class, float.class, explicit, float.class);
|
||||
} else if (expected == Double.class && internal) {
|
||||
return Cast.boxTo(char.class, double.class, explicit, double.class);
|
||||
return PainlessCast.boxTo(char.class, double.class, explicit, double.class);
|
||||
}
|
||||
} else if (actual == int.class) {
|
||||
if (expected == def.class) {
|
||||
return Cast.boxFrom(Integer.class, def.class, explicit, int.class);
|
||||
return PainlessCast.boxFrom(Integer.class, def.class, explicit, int.class);
|
||||
} else if (expected == Object.class && internal) {
|
||||
return Cast.boxFrom(Integer.class, Object.class, explicit, int.class);
|
||||
return PainlessCast.boxFrom(Integer.class, Object.class, explicit, int.class);
|
||||
} else if (expected == Number.class && internal) {
|
||||
return Cast.boxFrom(Integer.class, Number.class, explicit, int.class);
|
||||
return PainlessCast.boxFrom(Integer.class, Number.class, explicit, int.class);
|
||||
} else if (expected == byte.class && explicit) {
|
||||
return Cast.standard(int.class, byte.class, true);
|
||||
return PainlessCast.standard(int.class, byte.class, true);
|
||||
} else if (expected == char.class && explicit) {
|
||||
return Cast.standard(int.class, char.class, true);
|
||||
return PainlessCast.standard(int.class, char.class, true);
|
||||
} else if (expected == short.class && explicit) {
|
||||
return Cast.standard(int.class, short.class, true);
|
||||
return PainlessCast.standard(int.class, short.class, true);
|
||||
} else if (expected == long.class) {
|
||||
return Cast.standard(int.class, long.class, explicit);
|
||||
return PainlessCast.standard(int.class, long.class, explicit);
|
||||
} else if (expected == float.class) {
|
||||
return Cast.standard(int.class, float.class, explicit);
|
||||
return PainlessCast.standard(int.class, float.class, explicit);
|
||||
} else if (expected == double.class) {
|
||||
return Cast.standard(int.class, double.class, explicit);
|
||||
return PainlessCast.standard(int.class, double.class, explicit);
|
||||
} else if (expected == Byte.class && explicit && internal) {
|
||||
return Cast.boxTo(int.class, byte.class, true, byte.class);
|
||||
return PainlessCast.boxTo(int.class, byte.class, true, byte.class);
|
||||
} else if (expected == Short.class && explicit && internal) {
|
||||
return Cast.boxTo(int.class, short.class, true, short.class);
|
||||
return PainlessCast.boxTo(int.class, short.class, true, short.class);
|
||||
} else if (expected == Character.class && explicit && internal) {
|
||||
return Cast.boxTo(int.class, char.class, true, char.class);
|
||||
return PainlessCast.boxTo(int.class, char.class, true, char.class);
|
||||
} else if (expected == Integer.class && internal) {
|
||||
return Cast.boxTo(int.class, int.class, explicit, int.class);
|
||||
return PainlessCast.boxTo(int.class, int.class, explicit, int.class);
|
||||
} else if (expected == Long.class && internal) {
|
||||
return Cast.boxTo(int.class, long.class, explicit, long.class);
|
||||
return PainlessCast.boxTo(int.class, long.class, explicit, long.class);
|
||||
} else if (expected == Float.class && internal) {
|
||||
return Cast.boxTo(int.class, float.class, explicit, float.class);
|
||||
return PainlessCast.boxTo(int.class, float.class, explicit, float.class);
|
||||
} else if (expected == Double.class && internal) {
|
||||
return Cast.boxTo(int.class, double.class, explicit, double.class);
|
||||
return PainlessCast.boxTo(int.class, double.class, explicit, double.class);
|
||||
}
|
||||
} else if (actual == long.class) {
|
||||
if (expected == def.class) {
|
||||
return Cast.boxFrom(Long.class, def.class, explicit, long.class);
|
||||
return PainlessCast.boxFrom(Long.class, def.class, explicit, long.class);
|
||||
} else if (expected == Object.class && internal) {
|
||||
return Cast.boxFrom(Long.class, Object.class, explicit, long.class);
|
||||
return PainlessCast.boxFrom(Long.class, Object.class, explicit, long.class);
|
||||
} else if (expected == Number.class && internal) {
|
||||
return Cast.boxFrom(Long.class, Number.class, explicit, long.class);
|
||||
return PainlessCast.boxFrom(Long.class, Number.class, explicit, long.class);
|
||||
} else if (expected == byte.class && explicit) {
|
||||
return Cast.standard(long.class, byte.class, true);
|
||||
return PainlessCast.standard(long.class, byte.class, true);
|
||||
} else if (expected == char.class && explicit) {
|
||||
return Cast.standard(long.class, char.class, true);
|
||||
return PainlessCast.standard(long.class, char.class, true);
|
||||
} else if (expected == short.class && explicit) {
|
||||
return Cast.standard(long.class, short.class, true);
|
||||
return PainlessCast.standard(long.class, short.class, true);
|
||||
} else if (expected == int.class && explicit) {
|
||||
return Cast.standard(long.class, int.class, true);
|
||||
return PainlessCast.standard(long.class, int.class, true);
|
||||
} else if (expected == float.class) {
|
||||
return Cast.standard(long.class, float.class, explicit);
|
||||
return PainlessCast.standard(long.class, float.class, explicit);
|
||||
} else if (expected == double.class) {
|
||||
return Cast.standard(long.class, double.class, explicit);
|
||||
return PainlessCast.standard(long.class, double.class, explicit);
|
||||
} else if (expected == Byte.class && explicit && internal) {
|
||||
return Cast.boxTo(long.class, byte.class, true, byte.class);
|
||||
return PainlessCast.boxTo(long.class, byte.class, true, byte.class);
|
||||
} else if (expected == Short.class && explicit && internal) {
|
||||
return Cast.boxTo(long.class, short.class, true, short.class);
|
||||
return PainlessCast.boxTo(long.class, short.class, true, short.class);
|
||||
} else if (expected == Character.class && explicit && internal) {
|
||||
return Cast.boxTo(long.class, char.class, true, char.class);
|
||||
return PainlessCast.boxTo(long.class, char.class, true, char.class);
|
||||
} else if (expected == Integer.class && explicit && internal) {
|
||||
return Cast.boxTo(long.class, int.class, true, int.class);
|
||||
return PainlessCast.boxTo(long.class, int.class, true, int.class);
|
||||
} else if (expected == Long.class && internal) {
|
||||
return Cast.boxTo(long.class, long.class, explicit, long.class);
|
||||
return PainlessCast.boxTo(long.class, long.class, explicit, long.class);
|
||||
} else if (expected == Float.class && internal) {
|
||||
return Cast.boxTo(long.class, float.class, explicit, float.class);
|
||||
return PainlessCast.boxTo(long.class, float.class, explicit, float.class);
|
||||
} else if (expected == Double.class && internal) {
|
||||
return Cast.boxTo(long.class, double.class, explicit, double.class);
|
||||
return PainlessCast.boxTo(long.class, double.class, explicit, double.class);
|
||||
}
|
||||
} else if (actual == float.class) {
|
||||
if (expected == def.class) {
|
||||
return Cast.boxFrom(Float.class, def.class, explicit, float.class);
|
||||
return PainlessCast.boxFrom(Float.class, def.class, explicit, float.class);
|
||||
} else if (expected == Object.class && internal) {
|
||||
return Cast.boxFrom(Float.class, Object.class, explicit, float.class);
|
||||
return PainlessCast.boxFrom(Float.class, Object.class, explicit, float.class);
|
||||
} else if (expected == Number.class && internal) {
|
||||
return Cast.boxFrom(Float.class, Number.class, explicit, float.class);
|
||||
return PainlessCast.boxFrom(Float.class, Number.class, explicit, float.class);
|
||||
} else if (expected == byte.class && explicit) {
|
||||
return Cast.standard(float.class, byte.class, true);
|
||||
return PainlessCast.standard(float.class, byte.class, true);
|
||||
} else if (expected == char.class && explicit) {
|
||||
return Cast.standard(float.class, char.class, true);
|
||||
return PainlessCast.standard(float.class, char.class, true);
|
||||
} else if (expected == short.class && explicit) {
|
||||
return Cast.standard(float.class, short.class, true);
|
||||
return PainlessCast.standard(float.class, short.class, true);
|
||||
} else if (expected == int.class && explicit) {
|
||||
return Cast.standard(float.class, int.class, true);
|
||||
return PainlessCast.standard(float.class, int.class, true);
|
||||
} else if (expected == long.class && explicit) {
|
||||
return Cast.standard(float.class, long.class, true);
|
||||
return PainlessCast.standard(float.class, long.class, true);
|
||||
} else if (expected == double.class) {
|
||||
return Cast.standard(float.class, double.class, explicit);
|
||||
return PainlessCast.standard(float.class, double.class, explicit);
|
||||
} else if (expected == Byte.class && explicit && internal) {
|
||||
return Cast.boxTo(float.class, byte.class, true, byte.class);
|
||||
return PainlessCast.boxTo(float.class, byte.class, true, byte.class);
|
||||
} else if (expected == Short.class && explicit && internal) {
|
||||
return Cast.boxTo(float.class, short.class, true, short.class);
|
||||
return PainlessCast.boxTo(float.class, short.class, true, short.class);
|
||||
} else if (expected == Character.class && explicit && internal) {
|
||||
return Cast.boxTo(float.class, char.class, true, char.class);
|
||||
return PainlessCast.boxTo(float.class, char.class, true, char.class);
|
||||
} else if (expected == Integer.class && explicit && internal) {
|
||||
return Cast.boxTo(float.class, int.class, true, int.class);
|
||||
return PainlessCast.boxTo(float.class, int.class, true, int.class);
|
||||
} else if (expected == Long.class && explicit && internal) {
|
||||
return Cast.boxTo(float.class, long.class, true, long.class);
|
||||
return PainlessCast.boxTo(float.class, long.class, true, long.class);
|
||||
} else if (expected == Float.class && internal) {
|
||||
return Cast.boxTo(float.class, float.class, explicit, float.class);
|
||||
return PainlessCast.boxTo(float.class, float.class, explicit, float.class);
|
||||
} else if (expected == Double.class && internal) {
|
||||
return Cast.boxTo(float.class, double.class, explicit, double.class);
|
||||
return PainlessCast.boxTo(float.class, double.class, explicit, double.class);
|
||||
}
|
||||
} else if (actual == double.class) {
|
||||
if (expected == def.class) {
|
||||
return Cast.boxFrom(Double.class, def.class, explicit, double.class);
|
||||
return PainlessCast.boxFrom(Double.class, def.class, explicit, double.class);
|
||||
} else if (expected == Object.class && internal) {
|
||||
return Cast.boxFrom(Double.class, Object.class, explicit, double.class);
|
||||
return PainlessCast.boxFrom(Double.class, Object.class, explicit, double.class);
|
||||
} else if (expected == Number.class && internal) {
|
||||
return Cast.boxFrom(Double.class, Number.class, explicit, double.class);
|
||||
return PainlessCast.boxFrom(Double.class, Number.class, explicit, double.class);
|
||||
} else if (expected == byte.class && explicit) {
|
||||
return Cast.standard(double.class, byte.class, true);
|
||||
return PainlessCast.standard(double.class, byte.class, true);
|
||||
} else if (expected == char.class && explicit) {
|
||||
return Cast.standard(double.class, char.class, true);
|
||||
return PainlessCast.standard(double.class, char.class, true);
|
||||
} else if (expected == short.class && explicit) {
|
||||
return Cast.standard(double.class, short.class, true);
|
||||
return PainlessCast.standard(double.class, short.class, true);
|
||||
} else if (expected == int.class && explicit) {
|
||||
return Cast.standard(double.class, int.class, true);
|
||||
return PainlessCast.standard(double.class, int.class, true);
|
||||
} else if (expected == long.class && explicit) {
|
||||
return Cast.standard(double.class, long.class, true);
|
||||
return PainlessCast.standard(double.class, long.class, true);
|
||||
} else if (expected == float.class && explicit) {
|
||||
return Cast.standard(double.class, float.class, true);
|
||||
return PainlessCast.standard(double.class, float.class, true);
|
||||
} else if (expected == Byte.class && explicit && internal) {
|
||||
return Cast.boxTo(double.class, byte.class, true, byte.class);
|
||||
return PainlessCast.boxTo(double.class, byte.class, true, byte.class);
|
||||
} else if (expected == Short.class && explicit && internal) {
|
||||
return Cast.boxTo(double.class, short.class, true, short.class);
|
||||
return PainlessCast.boxTo(double.class, short.class, true, short.class);
|
||||
} else if (expected == Character.class && explicit && internal) {
|
||||
return Cast.boxTo(double.class, char.class, true, char.class);
|
||||
return PainlessCast.boxTo(double.class, char.class, true, char.class);
|
||||
} else if (expected == Integer.class && explicit && internal) {
|
||||
return Cast.boxTo(double.class, int.class, true, int.class);
|
||||
return PainlessCast.boxTo(double.class, int.class, true, int.class);
|
||||
} else if (expected == Long.class && explicit && internal) {
|
||||
return Cast.boxTo(double.class, long.class, true, long.class);
|
||||
return PainlessCast.boxTo(double.class, long.class, true, long.class);
|
||||
} else if (expected == Float.class && explicit && internal) {
|
||||
return Cast.boxTo(double.class, float.class, true, float.class);
|
||||
return PainlessCast.boxTo(double.class, float.class, true, float.class);
|
||||
} else if (expected == Double.class && internal) {
|
||||
return Cast.boxTo(double.class, double.class, explicit, double.class);
|
||||
return PainlessCast.boxTo(double.class, double.class, explicit, double.class);
|
||||
}
|
||||
} else if (actual == Boolean.class) {
|
||||
if (expected == boolean.class && internal) {
|
||||
return Cast.unboxFrom(boolean.class, boolean.class, explicit, boolean.class);
|
||||
return PainlessCast.unboxFrom(boolean.class, boolean.class, explicit, boolean.class);
|
||||
}
|
||||
} else if (actual == Byte.class) {
|
||||
if (expected == byte.class && internal) {
|
||||
return Cast.unboxFrom(byte.class, byte.class, explicit, byte.class);
|
||||
return PainlessCast.unboxFrom(byte.class, byte.class, explicit, byte.class);
|
||||
} else if (expected == short.class && internal) {
|
||||
return Cast.unboxFrom(byte.class, short.class, explicit, byte.class);
|
||||
return PainlessCast.unboxFrom(byte.class, short.class, explicit, byte.class);
|
||||
} else if (expected == char.class && explicit && internal) {
|
||||
return Cast.unboxFrom(byte.class, char.class, true, byte.class);
|
||||
return PainlessCast.unboxFrom(byte.class, char.class, true, byte.class);
|
||||
} else if (expected == int.class && internal) {
|
||||
return Cast.unboxFrom(byte.class, int.class, explicit, byte.class);
|
||||
return PainlessCast.unboxFrom(byte.class, int.class, explicit, byte.class);
|
||||
} else if (expected == long.class && internal) {
|
||||
return Cast.unboxFrom(byte.class, long.class, explicit, byte.class);
|
||||
return PainlessCast.unboxFrom(byte.class, long.class, explicit, byte.class);
|
||||
} else if (expected == float.class && internal) {
|
||||
return Cast.unboxFrom(byte.class, float.class, explicit, byte.class);
|
||||
return PainlessCast.unboxFrom(byte.class, float.class, explicit, byte.class);
|
||||
} else if (expected == double.class && internal) {
|
||||
return Cast.unboxFrom(byte.class, double.class, explicit, byte.class);
|
||||
return PainlessCast.unboxFrom(byte.class, double.class, explicit, byte.class);
|
||||
}
|
||||
} else if (actual == Short.class) {
|
||||
if (expected == byte.class && explicit && internal) {
|
||||
return Cast.unboxFrom(short.class, byte.class, true, short.class);
|
||||
return PainlessCast.unboxFrom(short.class, byte.class, true, short.class);
|
||||
} else if (expected == short.class && internal) {
|
||||
return Cast.unboxFrom(short.class, short.class, explicit, short.class);
|
||||
return PainlessCast.unboxFrom(short.class, short.class, explicit, short.class);
|
||||
} else if (expected == char.class && explicit && internal) {
|
||||
return Cast.unboxFrom(short.class, char.class, true, short.class);
|
||||
return PainlessCast.unboxFrom(short.class, char.class, true, short.class);
|
||||
} else if (expected == int.class && internal) {
|
||||
return Cast.unboxFrom(short.class, int.class, explicit, short.class);
|
||||
return PainlessCast.unboxFrom(short.class, int.class, explicit, short.class);
|
||||
} else if (expected == long.class && internal) {
|
||||
return Cast.unboxFrom(short.class, long.class, explicit, short.class);
|
||||
return PainlessCast.unboxFrom(short.class, long.class, explicit, short.class);
|
||||
} else if (expected == float.class && internal) {
|
||||
return Cast.unboxFrom(short.class, float.class, explicit, short.class);
|
||||
return PainlessCast.unboxFrom(short.class, float.class, explicit, short.class);
|
||||
} else if (expected == double.class && internal) {
|
||||
return Cast.unboxFrom(short.class, double.class, explicit, short.class);
|
||||
return PainlessCast.unboxFrom(short.class, double.class, explicit, short.class);
|
||||
}
|
||||
} else if (actual == Character.class) {
|
||||
if (expected == byte.class && explicit && internal) {
|
||||
return Cast.unboxFrom(char.class, byte.class, true, char.class);
|
||||
return PainlessCast.unboxFrom(char.class, byte.class, true, char.class);
|
||||
} else if (expected == short.class && explicit && internal) {
|
||||
return Cast.unboxFrom(char.class, short.class, true, char.class);
|
||||
return PainlessCast.unboxFrom(char.class, short.class, true, char.class);
|
||||
} else if (expected == char.class && internal) {
|
||||
return Cast.unboxFrom(char.class, char.class, explicit, char.class);
|
||||
return PainlessCast.unboxFrom(char.class, char.class, explicit, char.class);
|
||||
} else if (expected == int.class && internal) {
|
||||
return Cast.unboxFrom(char.class, int.class, explicit, char.class);
|
||||
return PainlessCast.unboxFrom(char.class, int.class, explicit, char.class);
|
||||
} else if (expected == long.class && internal) {
|
||||
return Cast.unboxFrom(char.class, long.class, explicit, char.class);
|
||||
return PainlessCast.unboxFrom(char.class, long.class, explicit, char.class);
|
||||
} else if (expected == float.class && internal) {
|
||||
return Cast.unboxFrom(char.class, float.class, explicit, char.class);
|
||||
return PainlessCast.unboxFrom(char.class, float.class, explicit, char.class);
|
||||
} else if (expected == double.class && internal) {
|
||||
return Cast.unboxFrom(char.class, double.class, explicit, char.class);
|
||||
return PainlessCast.unboxFrom(char.class, double.class, explicit, char.class);
|
||||
}
|
||||
} else if (actual == Integer.class) {
|
||||
if (expected == byte.class && explicit && internal) {
|
||||
return Cast.unboxFrom(int.class, byte.class, true, int.class);
|
||||
return PainlessCast.unboxFrom(int.class, byte.class, true, int.class);
|
||||
} else if (expected == short.class && explicit && internal) {
|
||||
return Cast.unboxFrom(int.class, short.class, true, int.class);
|
||||
return PainlessCast.unboxFrom(int.class, short.class, true, int.class);
|
||||
} else if (expected == char.class && explicit && internal) {
|
||||
return Cast.unboxFrom(int.class, char.class, true, int.class);
|
||||
return PainlessCast.unboxFrom(int.class, char.class, true, int.class);
|
||||
} else if (expected == int.class && internal) {
|
||||
return Cast.unboxFrom(int.class, int.class, explicit, int.class);
|
||||
return PainlessCast.unboxFrom(int.class, int.class, explicit, int.class);
|
||||
} else if (expected == long.class && internal) {
|
||||
return Cast.unboxFrom(int.class, long.class, explicit, int.class);
|
||||
return PainlessCast.unboxFrom(int.class, long.class, explicit, int.class);
|
||||
} else if (expected == float.class && internal) {
|
||||
return Cast.unboxFrom(int.class, float.class, explicit, int.class);
|
||||
return PainlessCast.unboxFrom(int.class, float.class, explicit, int.class);
|
||||
} else if (expected == double.class && internal) {
|
||||
return Cast.unboxFrom(int.class, double.class, explicit, int.class);
|
||||
return PainlessCast.unboxFrom(int.class, double.class, explicit, int.class);
|
||||
}
|
||||
} else if (actual == Long.class) {
|
||||
if (expected == byte.class && explicit && internal) {
|
||||
return Cast.unboxFrom(long.class, byte.class, true, long.class);
|
||||
return PainlessCast.unboxFrom(long.class, byte.class, true, long.class);
|
||||
} else if (expected == short.class && explicit && internal) {
|
||||
return Cast.unboxFrom(long.class, short.class, true, long.class);
|
||||
return PainlessCast.unboxFrom(long.class, short.class, true, long.class);
|
||||
} else if (expected == char.class && explicit && internal) {
|
||||
return Cast.unboxFrom(long.class, char.class, true, long.class);
|
||||
return PainlessCast.unboxFrom(long.class, char.class, true, long.class);
|
||||
} else if (expected == int.class && explicit && internal) {
|
||||
return Cast.unboxFrom(long.class, int.class, true, long.class);
|
||||
return PainlessCast.unboxFrom(long.class, int.class, true, long.class);
|
||||
} else if (expected == long.class && internal) {
|
||||
return Cast.unboxFrom(long.class, long.class, explicit, long.class);
|
||||
return PainlessCast.unboxFrom(long.class, long.class, explicit, long.class);
|
||||
} else if (expected == float.class && internal) {
|
||||
return Cast.unboxFrom(long.class, float.class, explicit, long.class);
|
||||
return PainlessCast.unboxFrom(long.class, float.class, explicit, long.class);
|
||||
} else if (expected == double.class && internal) {
|
||||
return Cast.unboxFrom(long.class, double.class, explicit, long.class);
|
||||
return PainlessCast.unboxFrom(long.class, double.class, explicit, long.class);
|
||||
}
|
||||
} else if (actual == Float.class) {
|
||||
if (expected == byte.class && explicit && internal) {
|
||||
return Cast.unboxFrom(float.class, byte.class, true, float.class);
|
||||
return PainlessCast.unboxFrom(float.class, byte.class, true, float.class);
|
||||
} else if (expected == short.class && explicit && internal) {
|
||||
return Cast.unboxFrom(float.class, short.class, true, float.class);
|
||||
return PainlessCast.unboxFrom(float.class, short.class, true, float.class);
|
||||
} else if (expected == char.class && explicit && internal) {
|
||||
return Cast.unboxFrom(float.class, char.class, true, float.class);
|
||||
return PainlessCast.unboxFrom(float.class, char.class, true, float.class);
|
||||
} else if (expected == int.class && explicit && internal) {
|
||||
return Cast.unboxFrom(float.class, int.class, true, float.class);
|
||||
return PainlessCast.unboxFrom(float.class, int.class, true, float.class);
|
||||
} else if (expected == long.class && explicit && internal) {
|
||||
return Cast.unboxFrom(float.class, long.class, true, float.class);
|
||||
return PainlessCast.unboxFrom(float.class, long.class, true, float.class);
|
||||
} else if (expected == float.class && internal) {
|
||||
return Cast.unboxFrom(float.class, float.class, explicit, float.class);
|
||||
return PainlessCast.unboxFrom(float.class, float.class, explicit, float.class);
|
||||
} else if (expected == double.class && internal) {
|
||||
return Cast.unboxFrom(float.class, double.class, explicit, float.class);
|
||||
return PainlessCast.unboxFrom(float.class, double.class, explicit, float.class);
|
||||
}
|
||||
} else if (actual == Double.class) {
|
||||
if (expected == byte.class && explicit && internal) {
|
||||
return Cast.unboxFrom(double.class, byte.class, true, double.class);
|
||||
return PainlessCast.unboxFrom(double.class, byte.class, true, double.class);
|
||||
} else if (expected == short.class && explicit && internal) {
|
||||
return Cast.unboxFrom(double.class, short.class, true, double.class);
|
||||
return PainlessCast.unboxFrom(double.class, short.class, true, double.class);
|
||||
} else if (expected == char.class && explicit && internal) {
|
||||
return Cast.unboxFrom(double.class, char.class, true, double.class);
|
||||
return PainlessCast.unboxFrom(double.class, char.class, true, double.class);
|
||||
} else if (expected == int.class && explicit && internal) {
|
||||
return Cast.unboxFrom(double.class, int.class, true, double.class);
|
||||
return PainlessCast.unboxFrom(double.class, int.class, true, double.class);
|
||||
} else if (expected == long.class && explicit && internal) {
|
||||
return Cast.unboxFrom(double.class, long.class, true, double.class);
|
||||
return PainlessCast.unboxFrom(double.class, long.class, true, double.class);
|
||||
} else if (expected == float.class && explicit && internal) {
|
||||
return Cast.unboxFrom(double.class, float.class, true, double.class);
|
||||
return PainlessCast.unboxFrom(double.class, float.class, true, double.class);
|
||||
} else if (expected == double.class && internal) {
|
||||
return Cast.unboxFrom(double.class, double.class, explicit, double.class);
|
||||
return PainlessCast.unboxFrom(double.class, double.class, explicit, double.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,14 +463,14 @@ public final class AnalyzerCaster {
|
|||
(actual != void.class && expected == def.class) ||
|
||||
expected.isAssignableFrom(actual) ||
|
||||
(actual.isAssignableFrom(expected) && explicit)) {
|
||||
return Cast.standard(actual, expected, explicit);
|
||||
return PainlessCast.standard(actual, expected, explicit);
|
||||
} else {
|
||||
throw location.createError(new ClassCastException(
|
||||
"Cannot cast from [" + Definition.ClassToName(actual) + "] to [" + Definition.ClassToName(expected) + "]."));
|
||||
"Cannot cast from [" + PainlessLookup.ClassToName(actual) + "] to [" + PainlessLookup.ClassToName(expected) + "]."));
|
||||
}
|
||||
}
|
||||
|
||||
public static Object constCast(Location location, Object constant, Cast cast) {
|
||||
public static Object constCast(Location location, Object constant, PainlessCast cast) {
|
||||
Class<?> fsort = cast.from;
|
||||
Class<?> tsort = cast.to;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.painless;
|
|||
|
||||
import org.elasticsearch.bootstrap.BootstrapInfo;
|
||||
import org.elasticsearch.painless.antlr.Walker;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.node.SSource;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.objectweb.asm.util.Printer;
|
||||
|
@ -70,26 +71,26 @@ final class Compiler {
|
|||
*/
|
||||
static final class Loader extends SecureClassLoader {
|
||||
private final AtomicInteger lambdaCounter = new AtomicInteger(0);
|
||||
private final Definition definition;
|
||||
private final PainlessLookup painlessLookup;
|
||||
|
||||
/**
|
||||
* @param parent The parent ClassLoader.
|
||||
*/
|
||||
Loader(ClassLoader parent, Definition definition) {
|
||||
Loader(ClassLoader parent, PainlessLookup painlessLookup) {
|
||||
super(parent);
|
||||
|
||||
this.definition = definition;
|
||||
this.painlessLookup = painlessLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will check to see if the {@link Class} has already been loaded when
|
||||
* the {@link Definition} was initially created. Allows for {@link Whitelist}ed
|
||||
* the {@link PainlessLookup} was initially created. Allows for {@link Whitelist}ed
|
||||
* classes to be loaded from other modules/plugins without a direct relationship
|
||||
* to the module's/plugin's {@link ClassLoader}.
|
||||
*/
|
||||
@Override
|
||||
public Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
Class<?> found = definition.getClassFromBinaryName(name);
|
||||
Class<?> found = painlessLookup.getClassFromBinaryName(name);
|
||||
|
||||
return found != null ? found : super.findClass(name);
|
||||
}
|
||||
|
@ -135,10 +136,10 @@ final class Compiler {
|
|||
|
||||
/**
|
||||
* Return a new {@link Loader} for a script using the
|
||||
* {@link Compiler}'s specified {@link Definition}.
|
||||
* {@link Compiler}'s specified {@link PainlessLookup}.
|
||||
*/
|
||||
public Loader createLoader(ClassLoader parent) {
|
||||
return new Loader(parent, definition);
|
||||
return new Loader(parent, painlessLookup);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,16 +150,16 @@ final class Compiler {
|
|||
/**
|
||||
* The whitelist the script will use.
|
||||
*/
|
||||
private final Definition definition;
|
||||
private final PainlessLookup painlessLookup;
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
* @param base The class/interface the script is guaranteed to derive/implement.
|
||||
* @param definition The whitelist the script will use.
|
||||
* @param painlessLookup The whitelist the script will use.
|
||||
*/
|
||||
Compiler(Class<?> base, Definition definition) {
|
||||
Compiler(Class<?> base, PainlessLookup painlessLookup) {
|
||||
this.base = base;
|
||||
this.definition = definition;
|
||||
this.painlessLookup = painlessLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,10 +177,10 @@ final class Compiler {
|
|||
" plugin if a script longer than this length is a requirement.");
|
||||
}
|
||||
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, base);
|
||||
SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, definition,
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, base);
|
||||
SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, painlessLookup,
|
||||
null);
|
||||
root.analyze(definition);
|
||||
root.analyze(painlessLookup);
|
||||
root.write();
|
||||
|
||||
try {
|
||||
|
@ -187,7 +188,7 @@ final class Compiler {
|
|||
clazz.getField("$NAME").set(null, name);
|
||||
clazz.getField("$SOURCE").set(null, source);
|
||||
clazz.getField("$STATEMENTS").set(null, root.getStatements());
|
||||
clazz.getField("$DEFINITION").set(null, definition);
|
||||
clazz.getField("$DEFINITION").set(null, painlessLookup);
|
||||
|
||||
return clazz.getConstructors()[0];
|
||||
} catch (Exception exception) { // Catch everything to let the user know this is something caused internally.
|
||||
|
@ -208,10 +209,10 @@ final class Compiler {
|
|||
" plugin if a script longer than this length is a requirement.");
|
||||
}
|
||||
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, base);
|
||||
SSource root = Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), name, source, settings, definition,
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, base);
|
||||
SSource root = Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), name, source, settings, painlessLookup,
|
||||
debugStream);
|
||||
root.analyze(definition);
|
||||
root.analyze(painlessLookup);
|
||||
root.write();
|
||||
|
||||
return root.getBytes();
|
||||
|
|
|
@ -19,13 +19,14 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethodKey;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
|
@ -60,14 +61,15 @@ public final class Def {
|
|||
*/
|
||||
@SuppressWarnings("unused") // getArrayLength() methods are are actually used, javac just does not know :)
|
||||
private static final class ArrayLengthHelper {
|
||||
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
|
||||
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private static final Map<Class<?>,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap(
|
||||
Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class,
|
||||
char[].class, float[].class, double[].class, Object[].class)
|
||||
.collect(Collectors.toMap(Function.identity(), type -> {
|
||||
try {
|
||||
return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "getArrayLength", MethodType.methodType(int.class, type));
|
||||
return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(
|
||||
PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "getArrayLength", MethodType.methodType(int.class, type));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
@ -116,17 +118,17 @@ public final class Def {
|
|||
static final MethodHandle JAVA9_ARRAY_LENGTH_MH_FACTORY;
|
||||
|
||||
static {
|
||||
final Lookup lookup = MethodHandles.publicLookup();
|
||||
final MethodHandles.Lookup methodHandlesLookup = MethodHandles.publicLookup();
|
||||
|
||||
try {
|
||||
MAP_GET = lookup.findVirtual(Map.class , "get", MethodType.methodType(Object.class, Object.class));
|
||||
MAP_PUT = lookup.findVirtual(Map.class , "put", MethodType.methodType(Object.class, Object.class, Object.class));
|
||||
LIST_GET = lookup.findVirtual(List.class, "get", MethodType.methodType(Object.class, int.class));
|
||||
LIST_SET = lookup.findVirtual(List.class, "set", MethodType.methodType(Object.class, int.class, Object.class));
|
||||
ITERATOR = lookup.findVirtual(Iterable.class, "iterator", MethodType.methodType(Iterator.class));
|
||||
MAP_INDEX_NORMALIZE = lookup.findStatic(Def.class, "mapIndexNormalize",
|
||||
MAP_GET = methodHandlesLookup.findVirtual(Map.class , "get", MethodType.methodType(Object.class, Object.class));
|
||||
MAP_PUT = methodHandlesLookup.findVirtual(Map.class , "put", MethodType.methodType(Object.class, Object.class, Object.class));
|
||||
LIST_GET = methodHandlesLookup.findVirtual(List.class, "get", MethodType.methodType(Object.class, int.class));
|
||||
LIST_SET = methodHandlesLookup.findVirtual(List.class, "set", MethodType.methodType(Object.class, int.class, Object.class));
|
||||
ITERATOR = methodHandlesLookup.findVirtual(Iterable.class, "iterator", MethodType.methodType(Iterator.class));
|
||||
MAP_INDEX_NORMALIZE = methodHandlesLookup.findStatic(Def.class, "mapIndexNormalize",
|
||||
MethodType.methodType(Object.class, Map.class, Object.class));
|
||||
LIST_INDEX_NORMALIZE = lookup.findStatic(Def.class, "listIndexNormalize",
|
||||
LIST_INDEX_NORMALIZE = methodHandlesLookup.findStatic(Def.class, "listIndexNormalize",
|
||||
MethodType.methodType(int.class, List.class, int.class));
|
||||
} catch (final ReflectiveOperationException roe) {
|
||||
throw new AssertionError(roe);
|
||||
|
@ -136,7 +138,7 @@ public final class Def {
|
|||
// https://bugs.openjdk.java.net/browse/JDK-8156915
|
||||
MethodHandle arrayLengthMHFactory;
|
||||
try {
|
||||
arrayLengthMHFactory = lookup.findStatic(MethodHandles.class, "arrayLength",
|
||||
arrayLengthMHFactory = methodHandlesLookup.findStatic(MethodHandles.class, "arrayLength",
|
||||
MethodType.methodType(MethodHandle.class, Class.class));
|
||||
} catch (final ReflectiveOperationException roe) {
|
||||
arrayLengthMHFactory = null;
|
||||
|
@ -174,31 +176,31 @@ public final class Def {
|
|||
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
|
||||
* Otherwise it returns the matching method.
|
||||
* <p>
|
||||
* @params definition the whitelist
|
||||
* @params painlessLookup the whitelist
|
||||
* @param receiverClass Class of the object to invoke the method on.
|
||||
* @param name Name of the method.
|
||||
* @param arity arity of method
|
||||
* @return matching method to invoke. never returns null.
|
||||
* @throws IllegalArgumentException if no matching whitelisted method was found.
|
||||
*/
|
||||
static Method lookupMethodInternal(Definition definition, Class<?> receiverClass, String name, int arity) {
|
||||
Definition.MethodKey key = new Definition.MethodKey(name, arity);
|
||||
static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class<?> receiverClass, String name, int arity) {
|
||||
PainlessMethodKey key = new PainlessMethodKey(name, arity);
|
||||
// check whitelist for matching method
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
Method method = struct.methods.get(key);
|
||||
PainlessMethod method = struct.methods.get(key);
|
||||
if (method != null) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
for (Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = definition.getPainlessStructFromJavaClass(iface);
|
||||
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
|
||||
|
||||
if (struct != null) {
|
||||
Method method = struct.methods.get(key);
|
||||
PainlessMethod method = struct.methods.get(key);
|
||||
if (method != null) {
|
||||
return method;
|
||||
}
|
||||
|
@ -220,8 +222,8 @@ public final class Def {
|
|||
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
|
||||
* Otherwise it returns a handle to the matching method.
|
||||
* <p>
|
||||
* @param definition the whitelist
|
||||
* @param lookup caller's lookup
|
||||
* @param painlessLookup the whitelist
|
||||
* @param methodHandlesLookup caller's lookup
|
||||
* @param callSiteType callsite's type
|
||||
* @param receiverClass Class of the object to invoke the method on.
|
||||
* @param name Name of the method.
|
||||
|
@ -230,13 +232,13 @@ public final class Def {
|
|||
* @throws IllegalArgumentException if no matching whitelisted method was found.
|
||||
* @throws Throwable if a method reference cannot be converted to an functional interface
|
||||
*/
|
||||
static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodType callSiteType,
|
||||
Class<?> receiverClass, String name, Object args[]) throws Throwable {
|
||||
static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, MethodType callSiteType,
|
||||
Class<?> receiverClass, String name, Object args[]) throws Throwable {
|
||||
String recipeString = (String) args[0];
|
||||
int numArguments = callSiteType.parameterCount();
|
||||
// simple case: no lambdas
|
||||
if (recipeString.isEmpty()) {
|
||||
return lookupMethodInternal(definition, receiverClass, name, numArguments - 1).handle;
|
||||
return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).handle;
|
||||
}
|
||||
|
||||
// convert recipe string to a bitset for convenience (the code below should be refactored...)
|
||||
|
@ -259,7 +261,7 @@ public final class Def {
|
|||
|
||||
// lookup the method with the proper arity, then we know everything (e.g. interface types of parameters).
|
||||
// based on these we can finally link any remaining lambdas that were deferred.
|
||||
Method method = lookupMethodInternal(definition, receiverClass, name, arity);
|
||||
PainlessMethod method = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
|
||||
MethodHandle handle = method.handle;
|
||||
|
||||
int replaced = 0;
|
||||
|
@ -283,8 +285,8 @@ public final class Def {
|
|||
if (signature.charAt(0) == 'S') {
|
||||
// the implementation is strongly typed, now that we know the interface type,
|
||||
// we have everything.
|
||||
filter = lookupReferenceInternal(definition,
|
||||
lookup,
|
||||
filter = lookupReferenceInternal(painlessLookup,
|
||||
methodHandlesLookup,
|
||||
interfaceType,
|
||||
type,
|
||||
call,
|
||||
|
@ -294,13 +296,13 @@ public final class Def {
|
|||
// this is dynamically based on the receiver type (and cached separately, underneath
|
||||
// this cache). It won't blow up since we never nest here (just references)
|
||||
MethodType nestedType = MethodType.methodType(interfaceType, captures);
|
||||
CallSite nested = DefBootstrap.bootstrap(definition,
|
||||
lookup,
|
||||
CallSite nested = DefBootstrap.bootstrap(painlessLookup,
|
||||
methodHandlesLookup,
|
||||
call,
|
||||
nestedType,
|
||||
0,
|
||||
DefBootstrap.REFERENCE,
|
||||
Definition.ClassToName(interfaceType));
|
||||
PainlessLookup.ClassToName(interfaceType));
|
||||
filter = nested.dynamicInvoker();
|
||||
} else {
|
||||
throw new AssertionError();
|
||||
|
@ -322,37 +324,37 @@ public final class Def {
|
|||
* This is just like LambdaMetaFactory, only with a dynamic type. The interface type is known,
|
||||
* so we simply need to lookup the matching implementation method based on receiver type.
|
||||
*/
|
||||
static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass,
|
||||
Class<?> receiverClass, String name) throws Throwable {
|
||||
Class<?> interfaceType = definition.getJavaClassFromPainlessType(interfaceClass);
|
||||
Method interfaceMethod = definition.getPainlessStructFromJavaClass(interfaceType).functionalMethod;
|
||||
static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String interfaceClass,
|
||||
Class<?> receiverClass, String name) throws Throwable {
|
||||
Class<?> interfaceType = painlessLookup.getJavaClassFromPainlessType(interfaceClass);
|
||||
PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(interfaceType).functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
|
||||
}
|
||||
int arity = interfaceMethod.arguments.size();
|
||||
Method implMethod = lookupMethodInternal(definition, receiverClass, name, arity);
|
||||
return lookupReferenceInternal(definition, lookup, interfaceType, implMethod.owner.name,
|
||||
PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
|
||||
return lookupReferenceInternal(painlessLookup, methodHandlesLookup, interfaceType, implMethod.owner.name,
|
||||
implMethod.name, receiverClass);
|
||||
}
|
||||
|
||||
/** Returns a method handle to an implementation of clazz, given method reference signature. */
|
||||
private static MethodHandle lookupReferenceInternal(Definition definition, Lookup lookup,
|
||||
Class<?> clazz, String type, String call, Class<?>... captures)
|
||||
private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup,
|
||||
Class<?> clazz, String type, String call, Class<?>... captures)
|
||||
throws Throwable {
|
||||
final FunctionRef ref;
|
||||
if ("this".equals(type)) {
|
||||
// user written method
|
||||
Method interfaceMethod = definition.getPainlessStructFromJavaClass(clazz).functionalMethod;
|
||||
PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(clazz).functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + Definition.ClassToName(clazz) + "], not a functional interface");
|
||||
"to [" + PainlessLookup.ClassToName(clazz) + "], not a functional interface");
|
||||
}
|
||||
int arity = interfaceMethod.arguments.size() + captures.length;
|
||||
final MethodHandle handle;
|
||||
try {
|
||||
MethodHandle accessor = lookup.findStaticGetter(lookup.lookupClass(),
|
||||
getUserFunctionHandleFieldName(call, arity),
|
||||
MethodHandle.class);
|
||||
MethodHandle accessor = methodHandlesLookup.findStaticGetter(methodHandlesLookup.lookupClass(),
|
||||
getUserFunctionHandleFieldName(call, arity),
|
||||
MethodHandle.class);
|
||||
handle = (MethodHandle)accessor.invokeExact();
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
// is it a synthetic method? If we generated the method ourselves, be more helpful. It can only fail
|
||||
|
@ -366,10 +368,10 @@ public final class Def {
|
|||
ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length);
|
||||
} else {
|
||||
// whitelist lookup
|
||||
ref = new FunctionRef(definition, clazz, type, call, captures.length);
|
||||
ref = new FunctionRef(painlessLookup, clazz, type, call, captures.length);
|
||||
}
|
||||
final CallSite callSite = LambdaBootstrap.lambdaBootstrap(
|
||||
lookup,
|
||||
methodHandlesLookup,
|
||||
ref.interfaceMethodName,
|
||||
ref.factoryMethodType,
|
||||
ref.interfaceMethodType,
|
||||
|
@ -407,16 +409,16 @@ public final class Def {
|
|||
* until it finds a matching whitelisted getter. If one is not found, it throws an exception.
|
||||
* Otherwise it returns a handle to the matching getter.
|
||||
* <p>
|
||||
* @param definition the whitelist
|
||||
* @param painlessLookup the whitelist
|
||||
* @param receiverClass Class of the object to retrieve the field from.
|
||||
* @param name Name of the field.
|
||||
* @return pointer to matching field. never returns null.
|
||||
* @throws IllegalArgumentException if no matching whitelisted field was found.
|
||||
*/
|
||||
static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass, String name) {
|
||||
static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
|
||||
// first try whitelist
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.getters.get(name);
|
||||
|
@ -426,7 +428,7 @@ public final class Def {
|
|||
}
|
||||
|
||||
for (final Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = definition.getPainlessStructFromJavaClass(iface);
|
||||
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.getters.get(name);
|
||||
|
@ -478,16 +480,16 @@ public final class Def {
|
|||
* until it finds a matching whitelisted setter. If one is not found, it throws an exception.
|
||||
* Otherwise it returns a handle to the matching setter.
|
||||
* <p>
|
||||
* @param definition the whitelist
|
||||
* @param painlessLookup the whitelist
|
||||
* @param receiverClass Class of the object to retrieve the field from.
|
||||
* @param name Name of the field.
|
||||
* @return pointer to matching field. never returns null.
|
||||
* @throws IllegalArgumentException if no matching whitelisted field was found.
|
||||
*/
|
||||
static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass, String name) {
|
||||
static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
|
||||
// first try whitelist
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.setters.get(name);
|
||||
|
@ -497,7 +499,7 @@ public final class Def {
|
|||
}
|
||||
|
||||
for (final Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = definition.getPainlessStructFromJavaClass(iface);
|
||||
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.setters.get(name);
|
||||
|
@ -592,14 +594,15 @@ public final class Def {
|
|||
*/
|
||||
@SuppressWarnings("unused") // iterator() methods are are actually used, javac just does not know :)
|
||||
private static final class ArrayIteratorHelper {
|
||||
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
|
||||
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private static final Map<Class<?>,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap(
|
||||
Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class,
|
||||
char[].class, float[].class, double[].class, Object[].class)
|
||||
.collect(Collectors.toMap(Function.identity(), type -> {
|
||||
try {
|
||||
return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "iterator", MethodType.methodType(Iterator.class, type));
|
||||
return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(
|
||||
PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "iterator", MethodType.methodType(Iterator.class, type));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
@ -860,14 +863,14 @@ public final class Def {
|
|||
*/
|
||||
@SuppressWarnings("unused") // normalizeIndex() methods are are actually used, javac just does not know :)
|
||||
private static final class ArrayIndexNormalizeHelper {
|
||||
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
|
||||
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private static final Map<Class<?>,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap(
|
||||
Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class,
|
||||
char[].class, float[].class, double[].class, Object[].class)
|
||||
.collect(Collectors.toMap(Function.identity(), type -> {
|
||||
try {
|
||||
return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "normalizeIndex",
|
||||
return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "normalizeIndex",
|
||||
MethodType.methodType(int.class, type, int.class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.MutableCallSite;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
|
@ -104,20 +104,21 @@ public final class DefBootstrap {
|
|||
/** maximum number of types before we go megamorphic */
|
||||
static final int MAX_DEPTH = 5;
|
||||
|
||||
private final Definition definition;
|
||||
private final Lookup lookup;
|
||||
private final PainlessLookup painlessLookup;
|
||||
private final MethodHandles.Lookup methodHandlesLookup;
|
||||
private final String name;
|
||||
private final int flavor;
|
||||
private final Object[] args;
|
||||
int depth; // pkg-protected for testing
|
||||
|
||||
PIC(Definition definition, Lookup lookup, String name, MethodType type, int initialDepth, int flavor, Object[] args) {
|
||||
PIC(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup,
|
||||
String name, MethodType type, int initialDepth, int flavor, Object[] args) {
|
||||
super(type);
|
||||
if (type.parameterType(0) != Object.class) {
|
||||
throw new BootstrapMethodError("The receiver type (1st arg) of invokedynamic descriptor must be Object.");
|
||||
}
|
||||
this.definition = definition;
|
||||
this.lookup = lookup;
|
||||
this.painlessLookup = painlessLookup;
|
||||
this.methodHandlesLookup = methodHandlesLookup;
|
||||
this.name = name;
|
||||
this.flavor = flavor;
|
||||
this.args = args;
|
||||
|
@ -144,11 +145,11 @@ public final class DefBootstrap {
|
|||
private MethodHandle lookup(int flavor, String name, Class<?> receiver) throws Throwable {
|
||||
switch(flavor) {
|
||||
case METHOD_CALL:
|
||||
return Def.lookupMethod(definition, lookup, type(), receiver, name, args);
|
||||
return Def.lookupMethod(painlessLookup, methodHandlesLookup, type(), receiver, name, args);
|
||||
case LOAD:
|
||||
return Def.lookupGetter(definition, receiver, name);
|
||||
return Def.lookupGetter(painlessLookup, receiver, name);
|
||||
case STORE:
|
||||
return Def.lookupSetter(definition, receiver, name);
|
||||
return Def.lookupSetter(painlessLookup, receiver, name);
|
||||
case ARRAY_LOAD:
|
||||
return Def.lookupArrayLoad(receiver);
|
||||
case ARRAY_STORE:
|
||||
|
@ -156,7 +157,7 @@ public final class DefBootstrap {
|
|||
case ITERATOR:
|
||||
return Def.lookupIterator(receiver);
|
||||
case REFERENCE:
|
||||
return Def.lookupReference(definition, lookup, (String) args[0], receiver, name);
|
||||
return Def.lookupReference(painlessLookup, methodHandlesLookup, (String) args[0], receiver, name);
|
||||
case INDEX_NORMALIZE:
|
||||
return Def.lookupIndexNormalize(receiver);
|
||||
default: throw new AssertionError();
|
||||
|
@ -216,17 +217,17 @@ public final class DefBootstrap {
|
|||
private static final MethodHandle FALLBACK;
|
||||
private static final MethodHandle MEGAMORPHIC_LOOKUP;
|
||||
static {
|
||||
final Lookup lookup = MethodHandles.lookup();
|
||||
final Lookup publicLookup = MethodHandles.publicLookup();
|
||||
final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup();
|
||||
final MethodHandles.Lookup publicMethodHandlesLookup = MethodHandles.publicLookup();
|
||||
try {
|
||||
CHECK_CLASS = lookup.findStatic(lookup.lookupClass(), "checkClass",
|
||||
MethodType.methodType(boolean.class, Class.class, Object.class));
|
||||
FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback",
|
||||
CHECK_CLASS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkClass",
|
||||
MethodType.methodType(boolean.class, Class.class, Object.class));
|
||||
FALLBACK = methodHandlesLookup.findVirtual(methodHandlesLookup.lookupClass(), "fallback",
|
||||
MethodType.methodType(Object.class, Object[].class));
|
||||
MethodHandle mh = publicLookup.findVirtual(ClassValue.class, "get",
|
||||
MethodHandle mh = publicMethodHandlesLookup.findVirtual(ClassValue.class, "get",
|
||||
MethodType.methodType(Object.class, Class.class));
|
||||
mh = MethodHandles.filterArguments(mh, 1,
|
||||
publicLookup.findVirtual(Object.class, "getClass", MethodType.methodType(Class.class)));
|
||||
publicMethodHandlesLookup.findVirtual(Object.class, "getClass", MethodType.methodType(Class.class)));
|
||||
MEGAMORPHIC_LOOKUP = mh.asType(mh.type().changeReturnType(MethodHandle.class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
|
@ -402,16 +403,16 @@ public final class DefBootstrap {
|
|||
private static final MethodHandle CHECK_BOTH;
|
||||
private static final MethodHandle FALLBACK;
|
||||
static {
|
||||
final Lookup lookup = MethodHandles.lookup();
|
||||
final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup();
|
||||
try {
|
||||
CHECK_LHS = lookup.findStatic(lookup.lookupClass(), "checkLHS",
|
||||
MethodType.methodType(boolean.class, Class.class, Object.class));
|
||||
CHECK_RHS = lookup.findStatic(lookup.lookupClass(), "checkRHS",
|
||||
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
|
||||
CHECK_BOTH = lookup.findStatic(lookup.lookupClass(), "checkBoth",
|
||||
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
|
||||
FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback",
|
||||
MethodType.methodType(Object.class, Object[].class));
|
||||
CHECK_LHS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkLHS",
|
||||
MethodType.methodType(boolean.class, Class.class, Object.class));
|
||||
CHECK_RHS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkRHS",
|
||||
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
|
||||
CHECK_BOTH = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkBoth",
|
||||
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
|
||||
FALLBACK = methodHandlesLookup.findVirtual(methodHandlesLookup.lookupClass(), "fallback",
|
||||
MethodType.methodType(Object.class, Object[].class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
@ -427,12 +428,12 @@ public final class DefBootstrap {
|
|||
* <li>{@code flavor}: type of dynamic call it is (and which part of whitelist to look at).
|
||||
* <li>{@code args}: flavor-specific args.
|
||||
* </ul>
|
||||
* And we take the {@link Definition} used to compile the script for whitelist checking.
|
||||
* And we take the {@link PainlessLookup} used to compile the script for whitelist checking.
|
||||
* <p>
|
||||
* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
||||
*/
|
||||
public static CallSite bootstrap(Definition definition, Lookup lookup, String name, MethodType type, int initialDepth, int flavor,
|
||||
Object... args) {
|
||||
public static CallSite bootstrap(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String name,
|
||||
MethodType type, int initialDepth, int flavor, Object... args) {
|
||||
// validate arguments
|
||||
switch(flavor) {
|
||||
// "function-call" like things get a polymorphic cache
|
||||
|
@ -451,7 +452,7 @@ public final class DefBootstrap {
|
|||
if (args.length != numLambdas + 1) {
|
||||
throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references");
|
||||
}
|
||||
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
|
||||
return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args);
|
||||
case LOAD:
|
||||
case STORE:
|
||||
case ARRAY_LOAD:
|
||||
|
@ -461,7 +462,7 @@ public final class DefBootstrap {
|
|||
if (args.length > 0) {
|
||||
throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor);
|
||||
}
|
||||
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
|
||||
return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args);
|
||||
case REFERENCE:
|
||||
if (args.length != 1) {
|
||||
throw new BootstrapMethodError("Invalid number of parameters for reference call");
|
||||
|
@ -469,7 +470,7 @@ public final class DefBootstrap {
|
|||
if (args[0] instanceof String == false) {
|
||||
throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]);
|
||||
}
|
||||
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
|
||||
return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args);
|
||||
|
||||
// operators get monomorphic cache, with a generic impl for a fallback
|
||||
case UNARY_OPERATOR:
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.elasticsearch.painless;
|
|||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -1070,7 +1069,7 @@ public class DefMath {
|
|||
}
|
||||
}
|
||||
|
||||
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
|
||||
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private static final Map<Class<?>,Map<String,MethodHandle>> TYPE_OP_MAPPING = Collections.unmodifiableMap(
|
||||
Stream.of(boolean.class, int.class, long.class, float.class, double.class, Object.class)
|
||||
|
@ -1081,26 +1080,26 @@ public class DefMath {
|
|||
MethodType binary = MethodType.methodType(type, type, type);
|
||||
MethodType comparison = MethodType.methodType(boolean.class, type, type);
|
||||
MethodType shift = MethodType.methodType(type, type, long.class);
|
||||
Class<?> clazz = PRIV_LOOKUP.lookupClass();
|
||||
map.put("not", PRIV_LOOKUP.findStatic(clazz, "not", unary));
|
||||
map.put("neg", PRIV_LOOKUP.findStatic(clazz, "neg", unary));
|
||||
map.put("plus", PRIV_LOOKUP.findStatic(clazz, "plus", unary));
|
||||
map.put("mul", PRIV_LOOKUP.findStatic(clazz, "mul", binary));
|
||||
map.put("div", PRIV_LOOKUP.findStatic(clazz, "div", binary));
|
||||
map.put("rem", PRIV_LOOKUP.findStatic(clazz, "rem", binary));
|
||||
map.put("add", PRIV_LOOKUP.findStatic(clazz, "add", binary));
|
||||
map.put("sub", PRIV_LOOKUP.findStatic(clazz, "sub", binary));
|
||||
map.put("and", PRIV_LOOKUP.findStatic(clazz, "and", binary));
|
||||
map.put("or", PRIV_LOOKUP.findStatic(clazz, "or", binary));
|
||||
map.put("xor", PRIV_LOOKUP.findStatic(clazz, "xor", binary));
|
||||
map.put("eq", PRIV_LOOKUP.findStatic(clazz, "eq", comparison));
|
||||
map.put("lt", PRIV_LOOKUP.findStatic(clazz, "lt", comparison));
|
||||
map.put("lte", PRIV_LOOKUP.findStatic(clazz, "lte", comparison));
|
||||
map.put("gt", PRIV_LOOKUP.findStatic(clazz, "gt", comparison));
|
||||
map.put("gte", PRIV_LOOKUP.findStatic(clazz, "gte", comparison));
|
||||
map.put("lsh", PRIV_LOOKUP.findStatic(clazz, "lsh", shift));
|
||||
map.put("rsh", PRIV_LOOKUP.findStatic(clazz, "rsh", shift));
|
||||
map.put("ush", PRIV_LOOKUP.findStatic(clazz, "ush", shift));
|
||||
Class<?> clazz = PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass();
|
||||
map.put("not", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "not", unary));
|
||||
map.put("neg", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "neg", unary));
|
||||
map.put("plus", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "plus", unary));
|
||||
map.put("mul", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "mul", binary));
|
||||
map.put("div", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "div", binary));
|
||||
map.put("rem", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "rem", binary));
|
||||
map.put("add", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "add", binary));
|
||||
map.put("sub", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "sub", binary));
|
||||
map.put("and", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "and", binary));
|
||||
map.put("or", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "or", binary));
|
||||
map.put("xor", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "xor", binary));
|
||||
map.put("eq", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "eq", comparison));
|
||||
map.put("lt", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lt", comparison));
|
||||
map.put("lte", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lte", comparison));
|
||||
map.put("gt", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "gt", comparison));
|
||||
map.put("gte", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "gte", comparison));
|
||||
map.put("lsh", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lsh", shift));
|
||||
map.put("rsh", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "rsh", shift));
|
||||
map.put("ush", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "ush", shift));
|
||||
return map;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
|
@ -1188,14 +1187,14 @@ public class DefMath {
|
|||
private static final MethodHandle DYNAMIC_CAST;
|
||||
private static final MethodHandle DYNAMIC_RECEIVER_CAST;
|
||||
static {
|
||||
final Lookup lookup = MethodHandles.lookup();
|
||||
final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup();
|
||||
try {
|
||||
DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(),
|
||||
"dynamicCast",
|
||||
MethodType.methodType(Object.class, Class.class, Object.class));
|
||||
DYNAMIC_RECEIVER_CAST = lookup.findStatic(lookup.lookupClass(),
|
||||
"dynamicReceiverCast",
|
||||
MethodType.methodType(Object.class, Object.class, Object.class));
|
||||
DYNAMIC_CAST = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(),
|
||||
"dynamicCast",
|
||||
MethodType.methodType(Object.class, Class.class, Object.class));
|
||||
DYNAMIC_RECEIVER_CAST = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(),
|
||||
"dynamicReceiverCast",
|
||||
MethodType.methodType(Object.class, Object.class, Object.class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethodKey;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
|
@ -55,9 +58,9 @@ public class FunctionRef {
|
|||
public final MethodType delegateMethodType;
|
||||
|
||||
/** interface method */
|
||||
public final Method interfaceMethod;
|
||||
public final PainlessMethod interfaceMethod;
|
||||
/** delegate method */
|
||||
public final Method delegateMethod;
|
||||
public final PainlessMethod delegateMethod;
|
||||
|
||||
/** factory method type descriptor */
|
||||
public final String factoryDescriptor;
|
||||
|
@ -71,15 +74,15 @@ public class FunctionRef {
|
|||
|
||||
/**
|
||||
* Creates a new FunctionRef, which will resolve {@code type::call} from the whitelist.
|
||||
* @param definition the whitelist against which this script is being compiled
|
||||
* @param painlessLookup the whitelist against which this script is being compiled
|
||||
* @param expected functional interface type to implement.
|
||||
* @param type the left hand side of a method reference expression
|
||||
* @param call the right hand side of a method reference expression
|
||||
* @param numCaptures number of captured arguments
|
||||
*/
|
||||
public FunctionRef(Definition definition, Class<?> expected, String type, String call, int numCaptures) {
|
||||
this(expected, definition.getPainlessStructFromJavaClass(expected).functionalMethod,
|
||||
lookup(definition, expected, type, call, numCaptures > 0), numCaptures);
|
||||
public FunctionRef(PainlessLookup painlessLookup, Class<?> expected, String type, String call, int numCaptures) {
|
||||
this(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
|
||||
lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +92,7 @@ public class FunctionRef {
|
|||
* @param delegateMethod implementation method
|
||||
* @param numCaptures number of captured arguments
|
||||
*/
|
||||
public FunctionRef(Class<?> expected, Method interfaceMethod, Method delegateMethod, int numCaptures) {
|
||||
public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) {
|
||||
MethodType delegateMethodType = delegateMethod.getMethodType();
|
||||
|
||||
interfaceMethodName = interfaceMethod.name;
|
||||
|
@ -135,7 +138,7 @@ public class FunctionRef {
|
|||
* It is for runtime use only.
|
||||
*/
|
||||
public FunctionRef(Class<?> expected,
|
||||
Method interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) {
|
||||
PainlessMethod interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) {
|
||||
interfaceMethodName = interfaceMethod.name;
|
||||
factoryMethodType = MethodType.methodType(expected,
|
||||
delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
|
||||
|
@ -158,25 +161,25 @@ public class FunctionRef {
|
|||
/**
|
||||
* Looks up {@code type::call} from the whitelist, and returns a matching method.
|
||||
*/
|
||||
private static Definition.Method lookup(Definition definition, Class<?> expected,
|
||||
String type, String call, boolean receiverCaptured) {
|
||||
private static PainlessMethod lookup(PainlessLookup painlessLookup, Class<?> expected,
|
||||
String type, String call, boolean receiverCaptured) {
|
||||
// check its really a functional interface
|
||||
// for e.g. Comparable
|
||||
Method method = definition.getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
if (method == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + Definition.ClassToName(expected) + "], not a functional interface");
|
||||
"to [" + PainlessLookup.ClassToName(expected) + "], not a functional interface");
|
||||
}
|
||||
|
||||
// lookup requested method
|
||||
Definition.Struct struct = definition.getPainlessStructFromJavaClass(definition.getJavaClassFromPainlessType(type));
|
||||
final Definition.Method impl;
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type));
|
||||
final PainlessMethod impl;
|
||||
// ctor ref
|
||||
if ("new".equals(call)) {
|
||||
impl = struct.constructors.get(new Definition.MethodKey("<init>", method.arguments.size()));
|
||||
impl = struct.constructors.get(new PainlessMethodKey("<init>", method.arguments.size()));
|
||||
} else {
|
||||
// look for a static impl first
|
||||
Definition.Method staticImpl = struct.staticMethods.get(new Definition.MethodKey(call, method.arguments.size()));
|
||||
PainlessMethod staticImpl = struct.staticMethods.get(new PainlessMethodKey(call, method.arguments.size()));
|
||||
if (staticImpl == null) {
|
||||
// otherwise a virtual impl
|
||||
final int arity;
|
||||
|
@ -187,7 +190,7 @@ public class FunctionRef {
|
|||
// receiver passed
|
||||
arity = method.arguments.size() - 1;
|
||||
}
|
||||
impl = struct.methods.get(new Definition.MethodKey(call, arity));
|
||||
impl = struct.methods.get(new PainlessMethodKey(call, arity));
|
||||
} else {
|
||||
impl = staticImpl;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethodKey;
|
||||
import org.elasticsearch.painless.ScriptClassInfo.MethodArgument;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -59,7 +60,7 @@ public final class Locals {
|
|||
*/
|
||||
public static Locals newLambdaScope(Locals programScope, Class<?> returnType, List<Parameter> parameters,
|
||||
int captureCount, int maxLoopCounter) {
|
||||
Locals locals = new Locals(programScope, programScope.definition, returnType, KEYWORDS);
|
||||
Locals locals = new Locals(programScope, programScope.painlessLookup, returnType, KEYWORDS);
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
Parameter parameter = parameters.get(i);
|
||||
// TODO: allow non-captures to be r/w:
|
||||
|
@ -78,7 +79,7 @@ public final class Locals {
|
|||
|
||||
/** Creates a new function scope inside the current scope */
|
||||
public static Locals newFunctionScope(Locals programScope, Class<?> returnType, List<Parameter> parameters, int maxLoopCounter) {
|
||||
Locals locals = new Locals(programScope, programScope.definition, returnType, KEYWORDS);
|
||||
Locals locals = new Locals(programScope, programScope.painlessLookup, returnType, KEYWORDS);
|
||||
for (Parameter parameter : parameters) {
|
||||
locals.addVariable(parameter.location, parameter.clazz, parameter.name, false);
|
||||
}
|
||||
|
@ -92,7 +93,7 @@ public final class Locals {
|
|||
/** Creates a new main method scope */
|
||||
public static Locals newMainMethodScope(ScriptClassInfo scriptClassInfo, Locals programScope, int maxLoopCounter) {
|
||||
Locals locals = new Locals(
|
||||
programScope, programScope.definition, scriptClassInfo.getExecuteMethodReturnType(), KEYWORDS);
|
||||
programScope, programScope.painlessLookup, scriptClassInfo.getExecuteMethodReturnType(), KEYWORDS);
|
||||
// This reference. Internal use only.
|
||||
locals.defineVariable(null, Object.class, THIS, true);
|
||||
|
||||
|
@ -109,9 +110,9 @@ public final class Locals {
|
|||
}
|
||||
|
||||
/** Creates a new program scope: the list of methods. It is the parent for all methods */
|
||||
public static Locals newProgramScope(Definition definition, Collection<Method> methods) {
|
||||
Locals locals = new Locals(null, definition, null, null);
|
||||
for (Method method : methods) {
|
||||
public static Locals newProgramScope(PainlessLookup painlessLookup, Collection<PainlessMethod> methods) {
|
||||
Locals locals = new Locals(null, painlessLookup, null, null);
|
||||
for (PainlessMethod method : methods) {
|
||||
locals.addMethod(method);
|
||||
}
|
||||
return locals;
|
||||
|
@ -142,8 +143,8 @@ public final class Locals {
|
|||
}
|
||||
|
||||
/** Looks up a method. Returns null if the method does not exist. */
|
||||
public Method getMethod(MethodKey key) {
|
||||
Method method = lookupMethod(key);
|
||||
public PainlessMethod getMethod(PainlessMethodKey key) {
|
||||
PainlessMethod method = lookupMethod(key);
|
||||
if (method != null) {
|
||||
return method;
|
||||
}
|
||||
|
@ -179,14 +180,14 @@ public final class Locals {
|
|||
}
|
||||
|
||||
/** Whitelist against which this script is being compiled. */
|
||||
public Definition getDefinition() {
|
||||
return definition;
|
||||
public PainlessLookup getPainlessLookup() {
|
||||
return painlessLookup;
|
||||
}
|
||||
|
||||
///// private impl
|
||||
|
||||
/** Whitelist against which this script is being compiled. */
|
||||
private final Definition definition;
|
||||
private final PainlessLookup painlessLookup;
|
||||
// parent scope
|
||||
private final Locals parent;
|
||||
// return type of this scope
|
||||
|
@ -198,21 +199,21 @@ public final class Locals {
|
|||
// variable name -> variable
|
||||
private Map<String,Variable> variables;
|
||||
// method name+arity -> methods
|
||||
private Map<MethodKey,Method> methods;
|
||||
private Map<PainlessMethodKey,PainlessMethod> methods;
|
||||
|
||||
/**
|
||||
* Create a new Locals
|
||||
*/
|
||||
private Locals(Locals parent) {
|
||||
this(parent, parent.definition, parent.returnType, parent.keywords);
|
||||
this(parent, parent.painlessLookup, parent.returnType, parent.keywords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Locals with specified return type
|
||||
*/
|
||||
private Locals(Locals parent, Definition definition, Class<?> returnType, Set<String> keywords) {
|
||||
private Locals(Locals parent, PainlessLookup painlessLookup, Class<?> returnType, Set<String> keywords) {
|
||||
this.parent = parent;
|
||||
this.definition = definition;
|
||||
this.painlessLookup = painlessLookup;
|
||||
this.returnType = returnType;
|
||||
this.keywords = keywords;
|
||||
if (parent == null) {
|
||||
|
@ -236,7 +237,7 @@ public final class Locals {
|
|||
}
|
||||
|
||||
/** Looks up a method at this scope only. Returns null if the method does not exist. */
|
||||
private Method lookupMethod(MethodKey key) {
|
||||
private PainlessMethod lookupMethod(PainlessMethodKey key) {
|
||||
if (methods == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -255,11 +256,11 @@ public final class Locals {
|
|||
return variable;
|
||||
}
|
||||
|
||||
private void addMethod(Method method) {
|
||||
private void addMethod(PainlessMethod method) {
|
||||
if (methods == null) {
|
||||
methods = new HashMap<>();
|
||||
}
|
||||
methods.put(new MethodKey(method.name, method.arguments.size()), method);
|
||||
methods.put(new PainlessMethodKey(method.name, method.arguments.size()), method);
|
||||
// TODO: check result
|
||||
}
|
||||
|
||||
|
@ -291,7 +292,7 @@ public final class Locals {
|
|||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("Variable[type=").append(Definition.ClassToName(clazz));
|
||||
b.append("Variable[type=").append(PainlessLookup.ClassToName(clazz));
|
||||
b.append(",name=").append(name);
|
||||
b.append(",slot=").append(slot);
|
||||
if (readonly) {
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessCast;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
@ -130,7 +130,7 @@ public final class MethodWriter extends GeneratorAdapter {
|
|||
mark(end);
|
||||
}
|
||||
|
||||
public void writeCast(Cast cast) {
|
||||
public void writeCast(PainlessCast cast) {
|
||||
if (cast != null) {
|
||||
if (cast.from == char.class && cast.to == String.class) {
|
||||
invokeStatic(UTILITY_TYPE, CHAR_TO_STRING);
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.api.Debug;
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -46,7 +48,7 @@ public class PainlessExplainError extends Error {
|
|||
/**
|
||||
* Headers to be added to the {@link ScriptException} for structured rendering.
|
||||
*/
|
||||
public Map<String, List<String>> getHeaders(Definition definition) {
|
||||
public Map<String, List<String>> getHeaders(PainlessLookup painlessLookup) {
|
||||
Map<String, List<String>> headers = new TreeMap<>();
|
||||
String toString = "null";
|
||||
String javaClassName = null;
|
||||
|
@ -54,7 +56,7 @@ public class PainlessExplainError extends Error {
|
|||
if (objectToExplain != null) {
|
||||
toString = objectToExplain.toString();
|
||||
javaClassName = objectToExplain.getClass().getName();
|
||||
Definition.Struct struct = definition.getPainlessStructFromJavaClass(objectToExplain.getClass());
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(objectToExplain.getClass());
|
||||
if (struct != null) {
|
||||
painlessClassName = struct.name;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.SpecialPermission;
|
|||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.painless.Compiler.Loader;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
|
@ -101,9 +102,9 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
|
|||
for (Map.Entry<ScriptContext<?>, List<Whitelist>> entry : contexts.entrySet()) {
|
||||
ScriptContext<?> context = entry.getKey();
|
||||
if (context.instanceClazz.equals(SearchScript.class) || context.instanceClazz.equals(ExecutableScript.class)) {
|
||||
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, new Definition(entry.getValue())));
|
||||
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, new PainlessLookup(entry.getValue())));
|
||||
} else {
|
||||
contextsToCompilers.put(context, new Compiler(context.instanceClazz, new Definition(entry.getValue())));
|
||||
contextsToCompilers.put(context, new Compiler(context.instanceClazz, new PainlessLookup(entry.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
@ -42,7 +44,7 @@ public class ScriptClassInfo {
|
|||
private final List<org.objectweb.asm.commons.Method> getMethods;
|
||||
private final List<Class<?>> getReturns;
|
||||
|
||||
public ScriptClassInfo(Definition definition, Class<?> baseClass) {
|
||||
public ScriptClassInfo(PainlessLookup painlessLookup, Class<?> baseClass) {
|
||||
this.baseClass = baseClass;
|
||||
|
||||
// Find the main method and the uses$argName methods
|
||||
|
@ -68,8 +70,9 @@ public class ScriptClassInfo {
|
|||
}
|
||||
if (m.getName().startsWith("get") && m.getName().equals("getClass") == false && Modifier.isStatic(m.getModifiers()) == false) {
|
||||
getReturns.add(
|
||||
definitionTypeForClass(definition, m.getReturnType(), componentType -> "[" + m.getName() + "] has unknown return type ["
|
||||
+ componentType.getName() + "]. Painless can only support getters with return types that are whitelisted."));
|
||||
definitionTypeForClass(painlessLookup, m.getReturnType(), componentType -> "[" + m.getName() + "] has unknown return " +
|
||||
"type [" + componentType.getName() + "]. Painless can only support getters with return types that are " +
|
||||
"whitelisted."));
|
||||
|
||||
getMethods.add(new org.objectweb.asm.commons.Method(m.getName(),
|
||||
MethodType.methodType(m.getReturnType()).toMethodDescriptorString()));
|
||||
|
@ -78,7 +81,7 @@ public class ScriptClassInfo {
|
|||
}
|
||||
MethodType methodType = MethodType.methodType(executeMethod.getReturnType(), executeMethod.getParameterTypes());
|
||||
this.executeMethod = new org.objectweb.asm.commons.Method(executeMethod.getName(), methodType.toMethodDescriptorString());
|
||||
executeMethodReturnType = definitionTypeForClass(definition, executeMethod.getReturnType(),
|
||||
executeMethodReturnType = definitionTypeForClass(painlessLookup, executeMethod.getReturnType(),
|
||||
componentType -> "Painless can only implement execute methods returning a whitelisted type but [" + baseClass.getName()
|
||||
+ "#execute] returns [" + componentType.getName() + "] which isn't whitelisted.");
|
||||
|
||||
|
@ -91,7 +94,7 @@ public class ScriptClassInfo {
|
|||
+ baseClass.getName() + "#execute] takes [1] argument.");
|
||||
}
|
||||
for (int arg = 0; arg < types.length; arg++) {
|
||||
arguments.add(methodArgument(definition, types[arg], argumentNamesConstant[arg]));
|
||||
arguments.add(methodArgument(painlessLookup, types[arg], argumentNamesConstant[arg]));
|
||||
}
|
||||
this.executeArguments = unmodifiableList(arguments);
|
||||
this.needsMethods = unmodifiableList(needsMethods);
|
||||
|
@ -171,22 +174,22 @@ public class ScriptClassInfo {
|
|||
}
|
||||
}
|
||||
|
||||
private MethodArgument methodArgument(Definition definition, Class<?> clazz, String argName) {
|
||||
Class<?> defClass = definitionTypeForClass(definition, clazz, componentType -> "[" + argName + "] is of unknown type ["
|
||||
private MethodArgument methodArgument(PainlessLookup painlessLookup, Class<?> clazz, String argName) {
|
||||
Class<?> defClass = definitionTypeForClass(painlessLookup, clazz, componentType -> "[" + argName + "] is of unknown type ["
|
||||
+ componentType.getName() + ". Painless interfaces can only accept arguments that are of whitelisted types.");
|
||||
return new MethodArgument(defClass, argName);
|
||||
}
|
||||
|
||||
private static Class<?> definitionTypeForClass(Definition definition, Class<?> type,
|
||||
Function<Class<?>, String> unknownErrorMessageSource) {
|
||||
type = Definition.ObjectClassTodefClass(type);
|
||||
private static Class<?> definitionTypeForClass(PainlessLookup painlessLookup, Class<?> type,
|
||||
Function<Class<?>, String> unknownErrorMessageSource) {
|
||||
type = PainlessLookup.ObjectClassTodefClass(type);
|
||||
Class<?> componentType = type;
|
||||
|
||||
while (componentType.isArray()) {
|
||||
componentType = componentType.getComponentType();
|
||||
}
|
||||
|
||||
if (definition.getPainlessStructFromJavaClass(componentType) == null) {
|
||||
if (painlessLookup.getPainlessStructFromJavaClass(componentType) == null) {
|
||||
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.api.Augmentation;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
@ -74,12 +75,12 @@ public final class WriterConstants {
|
|||
public static final Type STACK_OVERFLOW_ERROR_TYPE = Type.getType(StackOverflowError.class);
|
||||
public static final Type EXCEPTION_TYPE = Type.getType(Exception.class);
|
||||
public static final Type PAINLESS_EXPLAIN_ERROR_TYPE = Type.getType(PainlessExplainError.class);
|
||||
public static final Method PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD = getAsmMethod(Map.class, "getHeaders", Definition.class);
|
||||
public static final Method PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD = getAsmMethod(Map.class, "getHeaders", PainlessLookup.class);
|
||||
|
||||
public static final Type OBJECT_TYPE = Type.getType(Object.class);
|
||||
public static final Type BITSET_TYPE = Type.getType(BitSet.class);
|
||||
|
||||
public static final Type DEFINITION_TYPE = Type.getType(Definition.class);
|
||||
public static final Type DEFINITION_TYPE = Type.getType(PainlessLookup.class);
|
||||
|
||||
public static final Type COLLECTIONS_TYPE = Type.getType(Collections.class);
|
||||
public static final Method EMPTY_MAP_METHOD = getAsmMethod(Map.class, "emptyMap");
|
||||
|
@ -103,10 +104,10 @@ public final class WriterConstants {
|
|||
public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class);
|
||||
|
||||
/**
|
||||
* A Method instance for {@linkplain Pattern#compile}. This isn't available from Definition because we intentionally don't add it there
|
||||
* so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building regexes
|
||||
* because it can do it statically. This is both faster and prevents the script from doing something super slow like building a regex
|
||||
* per time it is run.
|
||||
* A Method instance for {@linkplain Pattern#compile}. This isn't available from PainlessLookup because we intentionally don't add it
|
||||
* there so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building
|
||||
* regexes because it can do it statically. This is both faster and prevents the script from doing something super slow like building a
|
||||
* regex per time it is run.
|
||||
*/
|
||||
public static final Method PATTERN_COMPILE = getAsmMethod(Pattern.class, "compile", String.class, int.class);
|
||||
public static final Method PATTERN_MATCHER = getAsmMethod(Matcher.class, "matcher", CharSequence.class);
|
||||
|
@ -118,7 +119,7 @@ public final class WriterConstants {
|
|||
static final Handle DEF_BOOTSTRAP_HANDLE = new Handle(Opcodes.H_INVOKESTATIC, CLASS_TYPE.getInternalName(), "$bootstrapDef",
|
||||
DEF_BOOTSTRAP_METHOD.getDescriptor(), false);
|
||||
public static final Type DEF_BOOTSTRAP_DELEGATE_TYPE = Type.getType(DefBootstrap.class);
|
||||
public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", Definition.class,
|
||||
public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", PainlessLookup.class,
|
||||
MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class);
|
||||
|
||||
public static final Type DEF_UTIL_TYPE = Type.getType(Def.class);
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.antlr.v4.runtime.CharStream;
|
|||
import org.antlr.v4.runtime.LexerNoViableAltException;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.misc.Interval;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
||||
/**
|
||||
|
@ -39,14 +39,14 @@ import org.elasticsearch.painless.Location;
|
|||
*/
|
||||
final class EnhancedPainlessLexer extends PainlessLexer {
|
||||
private final String sourceName;
|
||||
private final Definition definition;
|
||||
private final PainlessLookup painlessLookup;
|
||||
|
||||
private Token current = null;
|
||||
|
||||
EnhancedPainlessLexer(CharStream charStream, String sourceName, Definition definition) {
|
||||
EnhancedPainlessLexer(CharStream charStream, String sourceName, PainlessLookup painlessLookup) {
|
||||
super(charStream);
|
||||
this.sourceName = sourceName;
|
||||
this.definition = definition;
|
||||
this.painlessLookup = painlessLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,7 +75,7 @@ final class EnhancedPainlessLexer extends PainlessLexer {
|
|||
|
||||
@Override
|
||||
protected boolean isType(String name) {
|
||||
return definition.isSimplePainlessType(name);
|
||||
return painlessLookup.isSimplePainlessType(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.antlr.v4.runtime.Recognizer;
|
|||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.elasticsearch.painless.CompilerSettings;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Operation;
|
||||
|
@ -174,9 +174,9 @@ import java.util.List;
|
|||
public final class Walker extends PainlessParserBaseVisitor<ANode> {
|
||||
|
||||
public static SSource buildPainlessTree(ScriptClassInfo mainMethod, MainMethodReserved reserved, String sourceName,
|
||||
String sourceText, CompilerSettings settings, Definition definition,
|
||||
String sourceText, CompilerSettings settings, PainlessLookup painlessLookup,
|
||||
Printer debugStream) {
|
||||
return new Walker(mainMethod, reserved, sourceName, sourceText, settings, definition, debugStream).source;
|
||||
return new Walker(mainMethod, reserved, sourceName, sourceText, settings, painlessLookup, debugStream).source;
|
||||
}
|
||||
|
||||
private final ScriptClassInfo scriptClassInfo;
|
||||
|
@ -185,14 +185,14 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
|
|||
private final Printer debugStream;
|
||||
private final String sourceName;
|
||||
private final String sourceText;
|
||||
private final Definition definition;
|
||||
private final PainlessLookup painlessLookup;
|
||||
|
||||
private final Deque<Reserved> reserved = new ArrayDeque<>();
|
||||
private final Globals globals;
|
||||
private int syntheticCounter = 0;
|
||||
|
||||
private Walker(ScriptClassInfo scriptClassInfo, MainMethodReserved reserved, String sourceName, String sourceText,
|
||||
CompilerSettings settings, Definition definition, Printer debugStream) {
|
||||
CompilerSettings settings, PainlessLookup painlessLookup, Printer debugStream) {
|
||||
this.scriptClassInfo = scriptClassInfo;
|
||||
this.reserved.push(reserved);
|
||||
this.debugStream = debugStream;
|
||||
|
@ -200,13 +200,13 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
|
|||
this.sourceName = Location.computeSourceName(sourceName);
|
||||
this.sourceText = sourceText;
|
||||
this.globals = new Globals(new BitSet(sourceText.length()));
|
||||
this.definition = definition;
|
||||
this.painlessLookup = painlessLookup;
|
||||
this.source = (SSource)visit(buildAntlrTree(sourceText));
|
||||
}
|
||||
|
||||
private SourceContext buildAntlrTree(String source) {
|
||||
ANTLRInputStream stream = new ANTLRInputStream(source);
|
||||
PainlessLexer lexer = new EnhancedPainlessLexer(stream, sourceName, definition);
|
||||
PainlessLexer lexer = new EnhancedPainlessLexer(stream, sourceName, painlessLookup);
|
||||
PainlessParser parser = new PainlessParser(new CommonTokenStream(lexer));
|
||||
ParserErrorStrategy strategy = new ParserErrorStrategy(sourceName);
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
public class PainlessCast {
|
||||
|
||||
/** Create a standard cast with no boxing/unboxing. */
|
||||
public static PainlessCast standard(Class<?> from, Class<?> to, boolean explicit) {
|
||||
return new PainlessCast(from, to, explicit, null, null, null, null);
|
||||
}
|
||||
|
||||
/** Create a cast where the from type will be unboxed, and then the cast will be performed. */
|
||||
public static PainlessCast unboxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom) {
|
||||
return new PainlessCast(from, to, explicit, unboxFrom, null, null, null);
|
||||
}
|
||||
|
||||
/** Create a cast where the to type will be unboxed, and then the cast will be performed. */
|
||||
public static PainlessCast unboxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxTo) {
|
||||
return new PainlessCast(from, to, explicit, null, unboxTo, null, null);
|
||||
}
|
||||
|
||||
/** Create a cast where the from type will be boxed, and then the cast will be performed. */
|
||||
public static PainlessCast boxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> boxFrom) {
|
||||
return new PainlessCast(from, to, explicit, null, null, boxFrom, null);
|
||||
}
|
||||
|
||||
/** Create a cast where the to type will be boxed, and then the cast will be performed. */
|
||||
public static PainlessCast boxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> boxTo) {
|
||||
return new PainlessCast(from, to, explicit, null, null, null, boxTo);
|
||||
}
|
||||
|
||||
public final Class<?> from;
|
||||
public final Class<?> to;
|
||||
public final boolean explicit;
|
||||
public final Class<?> unboxFrom;
|
||||
public final Class<?> unboxTo;
|
||||
public final Class<?> boxFrom;
|
||||
public final Class<?> boxTo;
|
||||
|
||||
private PainlessCast(Class<?> from, Class<?> to, boolean explicit,
|
||||
Class<?> unboxFrom, Class<?> unboxTo, Class<?> boxFrom, Class<?> boxTo) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.explicit = explicit;
|
||||
this.unboxFrom = unboxFrom;
|
||||
this.unboxTo = unboxTo;
|
||||
this.boxFrom = boxFrom;
|
||||
this.boxTo = boxTo;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class PainlessClass {
|
||||
public final String name;
|
||||
public final Class<?> clazz;
|
||||
public final org.objectweb.asm.Type type;
|
||||
|
||||
public final Map<PainlessMethodKey, PainlessMethod> constructors;
|
||||
public final Map<PainlessMethodKey, PainlessMethod> staticMethods;
|
||||
public final Map<PainlessMethodKey, PainlessMethod> methods;
|
||||
|
||||
public final Map<String, PainlessField> staticMembers;
|
||||
public final Map<String, PainlessField> members;
|
||||
|
||||
public final Map<String, MethodHandle> getters;
|
||||
public final Map<String, MethodHandle> setters;
|
||||
|
||||
public final PainlessMethod functionalMethod;
|
||||
|
||||
PainlessClass(String name, Class<?> clazz, org.objectweb.asm.Type type) {
|
||||
this.name = name;
|
||||
this.clazz = clazz;
|
||||
this.type = type;
|
||||
|
||||
constructors = new HashMap<>();
|
||||
staticMethods = new HashMap<>();
|
||||
methods = new HashMap<>();
|
||||
|
||||
staticMembers = new HashMap<>();
|
||||
members = new HashMap<>();
|
||||
|
||||
getters = new HashMap<>();
|
||||
setters = new HashMap<>();
|
||||
|
||||
functionalMethod = null;
|
||||
}
|
||||
|
||||
private PainlessClass(PainlessClass struct, PainlessMethod functionalMethod) {
|
||||
name = struct.name;
|
||||
clazz = struct.clazz;
|
||||
type = struct.type;
|
||||
|
||||
constructors = Collections.unmodifiableMap(struct.constructors);
|
||||
staticMethods = Collections.unmodifiableMap(struct.staticMethods);
|
||||
methods = Collections.unmodifiableMap(struct.methods);
|
||||
|
||||
staticMembers = Collections.unmodifiableMap(struct.staticMembers);
|
||||
members = Collections.unmodifiableMap(struct.members);
|
||||
|
||||
getters = Collections.unmodifiableMap(struct.getters);
|
||||
setters = Collections.unmodifiableMap(struct.setters);
|
||||
|
||||
this.functionalMethod = functionalMethod;
|
||||
}
|
||||
|
||||
public PainlessClass freeze(PainlessMethod functionalMethod) {
|
||||
return new PainlessClass(this, functionalMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (object == null || getClass() != object.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PainlessClass struct = (PainlessClass)object;
|
||||
|
||||
return name.equals(struct.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
public final class PainlessField {
|
||||
public final String name;
|
||||
public final PainlessClass owner;
|
||||
public final Class<?> clazz;
|
||||
public final String javaName;
|
||||
public final int modifiers;
|
||||
public final MethodHandle getter;
|
||||
public final MethodHandle setter;
|
||||
|
||||
PainlessField(String name, String javaName, PainlessClass owner, Class<?> clazz, int modifiers,
|
||||
MethodHandle getter, MethodHandle setter) {
|
||||
this.name = name;
|
||||
this.javaName = javaName;
|
||||
this.owner = owner;
|
||||
this.clazz = clazz;
|
||||
this.modifiers = modifiers;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
}
|
|
@ -17,15 +17,17 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.elasticsearch.painless.spi.WhitelistClass;
|
||||
import org.elasticsearch.painless.spi.WhitelistConstructor;
|
||||
import org.elasticsearch.painless.spi.WhitelistField;
|
||||
import org.elasticsearch.painless.spi.WhitelistMethod;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -34,7 +36,6 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -42,10 +43,10 @@ import java.util.regex.Pattern;
|
|||
* The entire API for Painless. Also used as a whitelist for checking for legal
|
||||
* methods and fields during at both compile-time and runtime.
|
||||
*/
|
||||
public final class Definition {
|
||||
public final class PainlessLookup {
|
||||
|
||||
private static final Map<String, Method> methodCache = new HashMap<>();
|
||||
private static final Map<String, Field> fieldCache = new HashMap<>();
|
||||
private static final Map<String, PainlessMethod> methodCache = new HashMap<>();
|
||||
private static final Map<String, PainlessField> fieldCache = new HashMap<>();
|
||||
|
||||
private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
|
||||
|
||||
|
@ -56,306 +57,6 @@ public final class Definition {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Method {
|
||||
public final String name;
|
||||
public final Struct owner;
|
||||
public final Class<?> augmentation;
|
||||
public final Class<?> rtn;
|
||||
public final List<Class<?>> arguments;
|
||||
public final org.objectweb.asm.commons.Method method;
|
||||
public final int modifiers;
|
||||
public final MethodHandle handle;
|
||||
|
||||
public Method(String name, Struct owner, Class<?> augmentation, Class<?> rtn, List<Class<?>> arguments,
|
||||
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
|
||||
this.name = name;
|
||||
this.augmentation = augmentation;
|
||||
this.owner = owner;
|
||||
this.rtn = rtn;
|
||||
this.arguments = Collections.unmodifiableList(arguments);
|
||||
this.method = method;
|
||||
this.modifiers = modifiers;
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns MethodType for this method.
|
||||
* <p>
|
||||
* This works even for user-defined Methods (where the MethodHandle is null).
|
||||
*/
|
||||
public MethodType getMethodType() {
|
||||
// we have a methodhandle already (e.g. whitelisted class)
|
||||
// just return its type
|
||||
if (handle != null) {
|
||||
return handle.type();
|
||||
}
|
||||
// otherwise compute it
|
||||
final Class<?> params[];
|
||||
final Class<?> returnValue;
|
||||
if (augmentation != null) {
|
||||
// static method disguised as virtual/interface method
|
||||
params = new Class<?>[1 + arguments.size()];
|
||||
params[0] = augmentation;
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
params[i + 1] = defClassToObjectClass(arguments.get(i));
|
||||
}
|
||||
returnValue = defClassToObjectClass(rtn);
|
||||
} else if (Modifier.isStatic(modifiers)) {
|
||||
// static method: straightforward copy
|
||||
params = new Class<?>[arguments.size()];
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
params[i] = defClassToObjectClass(arguments.get(i));
|
||||
}
|
||||
returnValue = defClassToObjectClass(rtn);
|
||||
} else if ("<init>".equals(name)) {
|
||||
// constructor: returns the owner class
|
||||
params = new Class<?>[arguments.size()];
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
params[i] = defClassToObjectClass(arguments.get(i));
|
||||
}
|
||||
returnValue = owner.clazz;
|
||||
} else {
|
||||
// virtual/interface method: add receiver class
|
||||
params = new Class<?>[1 + arguments.size()];
|
||||
params[0] = owner.clazz;
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
params[i + 1] = defClassToObjectClass(arguments.get(i));
|
||||
}
|
||||
returnValue = defClassToObjectClass(rtn);
|
||||
}
|
||||
return MethodType.methodType(returnValue, params);
|
||||
}
|
||||
|
||||
public void write(MethodWriter writer) {
|
||||
final org.objectweb.asm.Type type;
|
||||
final Class<?> clazz;
|
||||
if (augmentation != null) {
|
||||
assert java.lang.reflect.Modifier.isStatic(modifiers);
|
||||
clazz = augmentation;
|
||||
type = org.objectweb.asm.Type.getType(augmentation);
|
||||
} else {
|
||||
clazz = owner.clazz;
|
||||
type = owner.type;
|
||||
}
|
||||
|
||||
if (java.lang.reflect.Modifier.isStatic(modifiers)) {
|
||||
// invokeStatic assumes that the owner class is not an interface, so this is a
|
||||
// special case for interfaces where the interface method boolean needs to be set to
|
||||
// true to reference the appropriate class constant when calling a static interface
|
||||
// method since java 8 did not check, but java 9 and 10 do
|
||||
if (java.lang.reflect.Modifier.isInterface(clazz.getModifiers())) {
|
||||
writer.visitMethodInsn(Opcodes.INVOKESTATIC,
|
||||
type.getInternalName(), name, getMethodType().toMethodDescriptorString(), true);
|
||||
} else {
|
||||
writer.invokeStatic(type, method);
|
||||
}
|
||||
} else if (java.lang.reflect.Modifier.isInterface(clazz.getModifiers())) {
|
||||
writer.invokeInterface(type, method);
|
||||
} else {
|
||||
writer.invokeVirtual(type, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Field {
|
||||
public final String name;
|
||||
public final Struct owner;
|
||||
public final Class<?> clazz;
|
||||
public final String javaName;
|
||||
public final int modifiers;
|
||||
private final MethodHandle getter;
|
||||
private final MethodHandle setter;
|
||||
|
||||
private Field(String name, String javaName, Struct owner, Class<?> clazz, int modifiers, MethodHandle getter, MethodHandle setter) {
|
||||
this.name = name;
|
||||
this.javaName = javaName;
|
||||
this.owner = owner;
|
||||
this.clazz = clazz;
|
||||
this.modifiers = modifiers;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: instead of hashing on this, we could have a 'next' pointer in Method itself, but it would make code more complex
|
||||
// please do *NOT* under any circumstances change this to be the crappy Tuple from elasticsearch!
|
||||
/**
|
||||
* Key for looking up a method.
|
||||
* <p>
|
||||
* Methods are keyed on both name and arity, and can be overloaded once per arity.
|
||||
* This allows signatures such as {@code String.indexOf(String) vs String.indexOf(String, int)}.
|
||||
* <p>
|
||||
* It is less flexible than full signature overloading where types can differ too, but
|
||||
* better than just the name, and overloading types adds complexity to users, too.
|
||||
*/
|
||||
public static final class MethodKey {
|
||||
public final String name;
|
||||
public final int arity;
|
||||
|
||||
/**
|
||||
* Create a new lookup key
|
||||
* @param name name of the method
|
||||
* @param arity number of parameters
|
||||
*/
|
||||
public MethodKey(String name, int arity) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.arity = arity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + arity;
|
||||
result = prime * result + name.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
MethodKey other = (MethodKey) obj;
|
||||
if (arity != other.arity) return false;
|
||||
if (!name.equals(other.name)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(name);
|
||||
sb.append('/');
|
||||
sb.append(arity);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Struct {
|
||||
public final String name;
|
||||
public final Class<?> clazz;
|
||||
public final org.objectweb.asm.Type type;
|
||||
|
||||
public final Map<MethodKey, Method> constructors;
|
||||
public final Map<MethodKey, Method> staticMethods;
|
||||
public final Map<MethodKey, Method> methods;
|
||||
|
||||
public final Map<String, Field> staticMembers;
|
||||
public final Map<String, Field> members;
|
||||
|
||||
public final Map<String, MethodHandle> getters;
|
||||
public final Map<String, MethodHandle> setters;
|
||||
|
||||
public final Method functionalMethod;
|
||||
|
||||
private Struct(String name, Class<?> clazz, org.objectweb.asm.Type type) {
|
||||
this.name = name;
|
||||
this.clazz = clazz;
|
||||
this.type = type;
|
||||
|
||||
constructors = new HashMap<>();
|
||||
staticMethods = new HashMap<>();
|
||||
methods = new HashMap<>();
|
||||
|
||||
staticMembers = new HashMap<>();
|
||||
members = new HashMap<>();
|
||||
|
||||
getters = new HashMap<>();
|
||||
setters = new HashMap<>();
|
||||
|
||||
functionalMethod = null;
|
||||
}
|
||||
|
||||
private Struct(Struct struct, Method functionalMethod) {
|
||||
name = struct.name;
|
||||
clazz = struct.clazz;
|
||||
type = struct.type;
|
||||
|
||||
constructors = Collections.unmodifiableMap(struct.constructors);
|
||||
staticMethods = Collections.unmodifiableMap(struct.staticMethods);
|
||||
methods = Collections.unmodifiableMap(struct.methods);
|
||||
|
||||
staticMembers = Collections.unmodifiableMap(struct.staticMembers);
|
||||
members = Collections.unmodifiableMap(struct.members);
|
||||
|
||||
getters = Collections.unmodifiableMap(struct.getters);
|
||||
setters = Collections.unmodifiableMap(struct.setters);
|
||||
|
||||
this.functionalMethod = functionalMethod;
|
||||
}
|
||||
|
||||
private Struct freeze(Method functionalMethod) {
|
||||
return new Struct(this, functionalMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (object == null || getClass() != object.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Struct struct = (Struct)object;
|
||||
|
||||
return name.equals(struct.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cast {
|
||||
|
||||
/** Create a standard cast with no boxing/unboxing. */
|
||||
public static Cast standard(Class<?> from, Class<?> to, boolean explicit) {
|
||||
return new Cast(from, to, explicit, null, null, null, null);
|
||||
}
|
||||
|
||||
/** Create a cast where the from type will be unboxed, and then the cast will be performed. */
|
||||
public static Cast unboxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom) {
|
||||
return new Cast(from, to, explicit, unboxFrom, null, null, null);
|
||||
}
|
||||
|
||||
/** Create a cast where the to type will be unboxed, and then the cast will be performed. */
|
||||
public static Cast unboxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxTo) {
|
||||
return new Cast(from, to, explicit, null, unboxTo, null, null);
|
||||
}
|
||||
|
||||
/** Create a cast where the from type will be boxed, and then the cast will be performed. */
|
||||
public static Cast boxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> boxFrom) {
|
||||
return new Cast(from, to, explicit, null, null, boxFrom, null);
|
||||
}
|
||||
|
||||
/** Create a cast where the to type will be boxed, and then the cast will be performed. */
|
||||
public static Cast boxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> boxTo) {
|
||||
return new Cast(from, to, explicit, null, null, null, boxTo);
|
||||
}
|
||||
|
||||
public final Class<?> from;
|
||||
public final Class<?> to;
|
||||
public final boolean explicit;
|
||||
public final Class<?> unboxFrom;
|
||||
public final Class<?> unboxTo;
|
||||
public final Class<?> boxFrom;
|
||||
public final Class<?> boxTo;
|
||||
|
||||
private Cast(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom, Class<?> unboxTo, Class<?> boxFrom, Class<?> boxTo) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.explicit = explicit;
|
||||
this.unboxFrom = unboxFrom;
|
||||
this.unboxTo = unboxTo;
|
||||
this.boxFrom = boxFrom;
|
||||
this.boxTo = boxTo;
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<?> getBoxedType(Class<?> clazz) {
|
||||
if (clazz == boolean.class) {
|
||||
return Boolean.class;
|
||||
|
@ -520,29 +221,29 @@ public final class Definition {
|
|||
return structName + fieldName + typeName;
|
||||
}
|
||||
|
||||
public Collection<Struct> getStructs() {
|
||||
public Collection<PainlessClass> getStructs() {
|
||||
return javaClassesToPainlessStructs.values();
|
||||
}
|
||||
|
||||
private final Map<String, Class<?>> painlessTypesToJavaClasses;
|
||||
private final Map<Class<?>, Struct> javaClassesToPainlessStructs;
|
||||
private final Map<Class<?>, PainlessClass> javaClassesToPainlessStructs;
|
||||
|
||||
public Definition(List<Whitelist> whitelists) {
|
||||
public PainlessLookup(List<Whitelist> whitelists) {
|
||||
painlessTypesToJavaClasses = new HashMap<>();
|
||||
javaClassesToPainlessStructs = new HashMap<>();
|
||||
|
||||
String origin = null;
|
||||
|
||||
painlessTypesToJavaClasses.put("def", def.class);
|
||||
javaClassesToPainlessStructs.put(def.class, new Struct("def", Object.class, Type.getType(Object.class)));
|
||||
javaClassesToPainlessStructs.put(def.class, new PainlessClass("def", Object.class, Type.getType(Object.class)));
|
||||
|
||||
try {
|
||||
// first iteration collects all the Painless type names that
|
||||
// are used for validation during the second iteration
|
||||
for (Whitelist whitelist : whitelists) {
|
||||
for (Whitelist.Struct whitelistStruct : whitelist.whitelistStructs) {
|
||||
for (WhitelistClass whitelistStruct : whitelist.whitelistStructs) {
|
||||
String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
|
||||
Struct painlessStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(painlessTypeName));
|
||||
PainlessClass painlessStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(painlessTypeName));
|
||||
|
||||
if (painlessStruct != null && painlessStruct.clazz.getName().equals(whitelistStruct.javaClassName) == false) {
|
||||
throw new IllegalArgumentException("struct [" + painlessStruct.name + "] cannot represent multiple classes " +
|
||||
|
@ -561,20 +262,20 @@ public final class Definition {
|
|||
// be available in Painless along with validating they exist and all their types have
|
||||
// been white-listed during the first iteration
|
||||
for (Whitelist whitelist : whitelists) {
|
||||
for (Whitelist.Struct whitelistStruct : whitelist.whitelistStructs) {
|
||||
for (WhitelistClass whitelistStruct : whitelist.whitelistStructs) {
|
||||
String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
|
||||
|
||||
for (Whitelist.Constructor whitelistConstructor : whitelistStruct.whitelistConstructors) {
|
||||
for (WhitelistConstructor whitelistConstructor : whitelistStruct.whitelistConstructors) {
|
||||
origin = whitelistConstructor.origin;
|
||||
addConstructor(painlessTypeName, whitelistConstructor);
|
||||
}
|
||||
|
||||
for (Whitelist.Method whitelistMethod : whitelistStruct.whitelistMethods) {
|
||||
for (WhitelistMethod whitelistMethod : whitelistStruct.whitelistMethods) {
|
||||
origin = whitelistMethod.origin;
|
||||
addMethod(whitelist.javaClassLoader, painlessTypeName, whitelistMethod);
|
||||
}
|
||||
|
||||
for (Whitelist.Field whitelistField : whitelistStruct.whitelistFields) {
|
||||
for (WhitelistField whitelistField : whitelistStruct.whitelistFields) {
|
||||
origin = whitelistField.origin;
|
||||
addField(painlessTypeName, whitelistField);
|
||||
}
|
||||
|
@ -587,7 +288,7 @@ public final class Definition {
|
|||
// goes through each Painless struct and determines the inheritance list,
|
||||
// and then adds all inherited types to the Painless struct's whitelist
|
||||
for (Class<?> javaClass : javaClassesToPainlessStructs.keySet()) {
|
||||
Struct painlessStruct = javaClassesToPainlessStructs.get(javaClass);
|
||||
PainlessClass painlessStruct = javaClassesToPainlessStructs.get(javaClass);
|
||||
|
||||
List<String> painlessSuperStructs = new ArrayList<>();
|
||||
Class<?> javaSuperClass = painlessStruct.clazz.getSuperclass();
|
||||
|
@ -598,7 +299,7 @@ public final class Definition {
|
|||
// adds super classes to the inheritance list
|
||||
if (javaSuperClass != null && javaSuperClass.isInterface() == false) {
|
||||
while (javaSuperClass != null) {
|
||||
Struct painlessSuperStruct = javaClassesToPainlessStructs.get(javaSuperClass);
|
||||
PainlessClass painlessSuperStruct = javaClassesToPainlessStructs.get(javaSuperClass);
|
||||
|
||||
if (painlessSuperStruct != null) {
|
||||
painlessSuperStructs.add(painlessSuperStruct.name);
|
||||
|
@ -614,7 +315,7 @@ public final class Definition {
|
|||
Class<?> javaInterfaceLookup = javaInteraceLookups.pop();
|
||||
|
||||
for (Class<?> javaSuperInterface : javaInterfaceLookup.getInterfaces()) {
|
||||
Struct painlessInterfaceStruct = javaClassesToPainlessStructs.get(javaSuperInterface);
|
||||
PainlessClass painlessInterfaceStruct = javaClassesToPainlessStructs.get(javaSuperInterface);
|
||||
|
||||
if (painlessInterfaceStruct != null) {
|
||||
String painlessInterfaceStructName = painlessInterfaceStruct.name;
|
||||
|
@ -635,7 +336,7 @@ public final class Definition {
|
|||
|
||||
// copies methods and fields from Object into interface types
|
||||
if (painlessStruct.clazz.isInterface() || (def.class.getSimpleName()).equals(painlessStruct.name)) {
|
||||
Struct painlessObjectStruct = javaClassesToPainlessStructs.get(Object.class);
|
||||
PainlessClass painlessObjectStruct = javaClassesToPainlessStructs.get(Object.class);
|
||||
|
||||
if (painlessObjectStruct != null) {
|
||||
copyStruct(painlessStruct.name, Collections.singletonList(painlessObjectStruct.name));
|
||||
|
@ -644,17 +345,17 @@ public final class Definition {
|
|||
}
|
||||
|
||||
// precompute runtime classes
|
||||
for (Struct painlessStruct : javaClassesToPainlessStructs.values()) {
|
||||
for (PainlessClass painlessStruct : javaClassesToPainlessStructs.values()) {
|
||||
addRuntimeClass(painlessStruct);
|
||||
}
|
||||
|
||||
// copy all structs to make them unmodifiable for outside users:
|
||||
for (Map.Entry<Class<?>,Struct> entry : javaClassesToPainlessStructs.entrySet()) {
|
||||
for (Map.Entry<Class<?>,PainlessClass> entry : javaClassesToPainlessStructs.entrySet()) {
|
||||
entry.setValue(entry.getValue().freeze(computeFunctionalInterfaceMethod(entry.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
private void addStruct(ClassLoader whitelistClassLoader, Whitelist.Struct whitelistStruct) {
|
||||
private void addStruct(ClassLoader whitelistClassLoader, WhitelistClass whitelistStruct) {
|
||||
String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
|
||||
String importedPainlessTypeName = painlessTypeName;
|
||||
|
||||
|
@ -688,10 +389,10 @@ public final class Definition {
|
|||
}
|
||||
}
|
||||
|
||||
Struct existingStruct = javaClassesToPainlessStructs.get(javaClass);
|
||||
PainlessClass existingStruct = javaClassesToPainlessStructs.get(javaClass);
|
||||
|
||||
if (existingStruct == null) {
|
||||
Struct struct = new Struct(painlessTypeName, javaClass, org.objectweb.asm.Type.getType(javaClass));
|
||||
PainlessClass struct = new PainlessClass(painlessTypeName, javaClass, org.objectweb.asm.Type.getType(javaClass));
|
||||
painlessTypesToJavaClasses.put(painlessTypeName, javaClass);
|
||||
javaClassesToPainlessStructs.put(javaClass, struct);
|
||||
} else if (existingStruct.clazz.equals(javaClass) == false) {
|
||||
|
@ -725,8 +426,8 @@ public final class Definition {
|
|||
}
|
||||
}
|
||||
|
||||
private void addConstructor(String ownerStructName, Whitelist.Constructor whitelistConstructor) {
|
||||
Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
|
||||
private void addConstructor(String ownerStructName, WhitelistConstructor whitelistConstructor) {
|
||||
PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
|
||||
|
||||
if (ownerStruct == null) {
|
||||
throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for constructor with " +
|
||||
|
@ -760,8 +461,8 @@ public final class Definition {
|
|||
" with constructor parameters " + whitelistConstructor.painlessParameterTypeNames, exception);
|
||||
}
|
||||
|
||||
MethodKey painlessMethodKey = new MethodKey("<init>", whitelistConstructor.painlessParameterTypeNames.size());
|
||||
Method painlessConstructor = ownerStruct.constructors.get(painlessMethodKey);
|
||||
PainlessMethodKey painlessMethodKey = new PainlessMethodKey("<init>", whitelistConstructor.painlessParameterTypeNames.size());
|
||||
PainlessMethod painlessConstructor = ownerStruct.constructors.get(painlessMethodKey);
|
||||
|
||||
if (painlessConstructor == null) {
|
||||
org.objectweb.asm.commons.Method asmConstructor = org.objectweb.asm.commons.Method.getMethod(javaConstructor);
|
||||
|
@ -775,7 +476,7 @@ public final class Definition {
|
|||
}
|
||||
|
||||
painlessConstructor = methodCache.computeIfAbsent(buildMethodCacheKey(ownerStruct.name, "<init>", painlessParametersTypes),
|
||||
key -> new Method("<init>", ownerStruct, null, void.class, painlessParametersTypes,
|
||||
key -> new PainlessMethod("<init>", ownerStruct, null, void.class, painlessParametersTypes,
|
||||
asmConstructor, javaConstructor.getModifiers(), javaHandle));
|
||||
ownerStruct.constructors.put(painlessMethodKey, painlessConstructor);
|
||||
} else if (painlessConstructor.arguments.equals(painlessParametersTypes) == false){
|
||||
|
@ -785,8 +486,8 @@ public final class Definition {
|
|||
}
|
||||
}
|
||||
|
||||
private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, Whitelist.Method whitelistMethod) {
|
||||
Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
|
||||
private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, WhitelistMethod whitelistMethod) {
|
||||
PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
|
||||
|
||||
if (ownerStruct == null) {
|
||||
throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for method with " +
|
||||
|
@ -864,10 +565,11 @@ public final class Definition {
|
|||
"and parameters " + whitelistMethod.painlessParameterTypeNames);
|
||||
}
|
||||
|
||||
MethodKey painlessMethodKey = new MethodKey(whitelistMethod.javaMethodName, whitelistMethod.painlessParameterTypeNames.size());
|
||||
PainlessMethodKey painlessMethodKey =
|
||||
new PainlessMethodKey(whitelistMethod.javaMethodName, whitelistMethod.painlessParameterTypeNames.size());
|
||||
|
||||
if (javaAugmentedClass == null && Modifier.isStatic(javaMethod.getModifiers())) {
|
||||
Method painlessMethod = ownerStruct.staticMethods.get(painlessMethodKey);
|
||||
PainlessMethod painlessMethod = ownerStruct.staticMethods.get(painlessMethodKey);
|
||||
|
||||
if (painlessMethod == null) {
|
||||
org.objectweb.asm.commons.Method asmMethod = org.objectweb.asm.commons.Method.getMethod(javaMethod);
|
||||
|
@ -882,8 +584,8 @@ public final class Definition {
|
|||
|
||||
painlessMethod = methodCache.computeIfAbsent(
|
||||
buildMethodCacheKey(ownerStruct.name, whitelistMethod.javaMethodName, painlessParametersTypes),
|
||||
key -> new Method(whitelistMethod.javaMethodName, ownerStruct, null, painlessReturnClass, painlessParametersTypes,
|
||||
asmMethod, javaMethod.getModifiers(), javaMethodHandle));
|
||||
key -> new PainlessMethod(whitelistMethod.javaMethodName, ownerStruct, null, painlessReturnClass,
|
||||
painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle));
|
||||
ownerStruct.staticMethods.put(painlessMethodKey, painlessMethod);
|
||||
} else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn == painlessReturnClass &&
|
||||
painlessMethod.arguments.equals(painlessParametersTypes)) == false) {
|
||||
|
@ -893,7 +595,7 @@ public final class Definition {
|
|||
"and parameters " + painlessParametersTypes + " and " + painlessMethod.arguments);
|
||||
}
|
||||
} else {
|
||||
Method painlessMethod = ownerStruct.methods.get(painlessMethodKey);
|
||||
PainlessMethod painlessMethod = ownerStruct.methods.get(painlessMethodKey);
|
||||
|
||||
if (painlessMethod == null) {
|
||||
org.objectweb.asm.commons.Method asmMethod = org.objectweb.asm.commons.Method.getMethod(javaMethod);
|
||||
|
@ -908,7 +610,7 @@ public final class Definition {
|
|||
|
||||
painlessMethod = methodCache.computeIfAbsent(
|
||||
buildMethodCacheKey(ownerStruct.name, whitelistMethod.javaMethodName, painlessParametersTypes),
|
||||
key -> new Method(whitelistMethod.javaMethodName, ownerStruct, javaAugmentedClass, painlessReturnClass,
|
||||
key -> new PainlessMethod(whitelistMethod.javaMethodName, ownerStruct, javaAugmentedClass, painlessReturnClass,
|
||||
painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle));
|
||||
ownerStruct.methods.put(painlessMethodKey, painlessMethod);
|
||||
} else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn.equals(painlessReturnClass) &&
|
||||
|
@ -921,8 +623,8 @@ public final class Definition {
|
|||
}
|
||||
}
|
||||
|
||||
private void addField(String ownerStructName, Whitelist.Field whitelistField) {
|
||||
Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
|
||||
private void addField(String ownerStructName, WhitelistField whitelistField) {
|
||||
PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
|
||||
|
||||
if (ownerStruct == null) {
|
||||
throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for method with " +
|
||||
|
@ -958,12 +660,12 @@ public final class Definition {
|
|||
"with owner struct [" + ownerStruct.name + "] is not final");
|
||||
}
|
||||
|
||||
Field painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName);
|
||||
PainlessField painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName);
|
||||
|
||||
if (painlessField == null) {
|
||||
painlessField = fieldCache.computeIfAbsent(
|
||||
buildFieldCacheKey(ownerStruct.name, whitelistField.javaFieldName, painlessFieldClass.getName()),
|
||||
key -> new Field(whitelistField.javaFieldName, javaField.getName(),
|
||||
key -> new PainlessField(whitelistField.javaFieldName, javaField.getName(),
|
||||
ownerStruct, painlessFieldClass, javaField.getModifiers(), null, null));
|
||||
ownerStruct.staticMembers.put(whitelistField.javaFieldName, painlessField);
|
||||
} else if (painlessField.clazz != painlessFieldClass) {
|
||||
|
@ -987,12 +689,12 @@ public final class Definition {
|
|||
" not found for class [" + ownerStruct.clazz.getName() + "].");
|
||||
}
|
||||
|
||||
Field painlessField = ownerStruct.members.get(whitelistField.javaFieldName);
|
||||
PainlessField painlessField = ownerStruct.members.get(whitelistField.javaFieldName);
|
||||
|
||||
if (painlessField == null) {
|
||||
painlessField = fieldCache.computeIfAbsent(
|
||||
buildFieldCacheKey(ownerStruct.name, whitelistField.javaFieldName, painlessFieldClass.getName()),
|
||||
key -> new Field(whitelistField.javaFieldName, javaField.getName(),
|
||||
key -> new PainlessField(whitelistField.javaFieldName, javaField.getName(),
|
||||
ownerStruct, painlessFieldClass, javaField.getModifiers(), javaMethodHandleGetter, javaMethodHandleSetter));
|
||||
ownerStruct.members.put(whitelistField.javaFieldName, painlessField);
|
||||
} else if (painlessField.clazz != painlessFieldClass) {
|
||||
|
@ -1003,14 +705,14 @@ public final class Definition {
|
|||
}
|
||||
|
||||
private void copyStruct(String struct, List<String> children) {
|
||||
final Struct owner = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(struct));
|
||||
final PainlessClass owner = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(struct));
|
||||
|
||||
if (owner == null) {
|
||||
throw new IllegalArgumentException("Owner struct [" + struct + "] not defined for copy.");
|
||||
}
|
||||
|
||||
for (int count = 0; count < children.size(); ++count) {
|
||||
final Struct child = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(children.get(count)));
|
||||
final PainlessClass child = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(children.get(count)));
|
||||
|
||||
if (child == null) {
|
||||
throw new IllegalArgumentException("Child struct [" + children.get(count) + "]" +
|
||||
|
@ -1022,9 +724,9 @@ public final class Definition {
|
|||
" is not a super type of owner struct [" + owner.name + "] in copy.");
|
||||
}
|
||||
|
||||
for (Map.Entry<MethodKey,Method> kvPair : child.methods.entrySet()) {
|
||||
MethodKey methodKey = kvPair.getKey();
|
||||
Method method = kvPair.getValue();
|
||||
for (Map.Entry<PainlessMethodKey,PainlessMethod> kvPair : child.methods.entrySet()) {
|
||||
PainlessMethodKey methodKey = kvPair.getKey();
|
||||
PainlessMethod method = kvPair.getValue();
|
||||
if (owner.methods.get(methodKey) == null) {
|
||||
// TODO: some of these are no longer valid or outright don't work
|
||||
// TODO: since classes may not come from the Painless classloader
|
||||
|
@ -1076,10 +778,10 @@ public final class Definition {
|
|||
}
|
||||
}
|
||||
|
||||
for (Field field : child.members.values()) {
|
||||
for (PainlessField field : child.members.values()) {
|
||||
if (owner.members.get(field.name) == null) {
|
||||
owner.members.put(field.name,
|
||||
new Field(field.name, field.javaName, owner, field.clazz, field.modifiers, field.getter, field.setter));
|
||||
new PainlessField(field.name, field.javaName, owner, field.clazz, field.modifiers, field.getter, field.setter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1088,11 +790,11 @@ public final class Definition {
|
|||
/**
|
||||
* Precomputes a more efficient structure for dynamic method/field access.
|
||||
*/
|
||||
private void addRuntimeClass(final Struct struct) {
|
||||
private void addRuntimeClass(final PainlessClass struct) {
|
||||
// add all getters/setters
|
||||
for (Map.Entry<MethodKey, Method> method : struct.methods.entrySet()) {
|
||||
for (Map.Entry<PainlessMethodKey, PainlessMethod> method : struct.methods.entrySet()) {
|
||||
String name = method.getKey().name;
|
||||
Method m = method.getValue();
|
||||
PainlessMethod m = method.getValue();
|
||||
|
||||
if (m.arguments.size() == 0 &&
|
||||
name.startsWith("get") &&
|
||||
|
@ -1124,14 +826,14 @@ public final class Definition {
|
|||
}
|
||||
|
||||
// add all members
|
||||
for (Map.Entry<String, Field> member : struct.members.entrySet()) {
|
||||
for (Map.Entry<String, PainlessField> member : struct.members.entrySet()) {
|
||||
struct.getters.put(member.getKey(), member.getValue().getter);
|
||||
struct.setters.put(member.getKey(), member.getValue().setter);
|
||||
}
|
||||
}
|
||||
|
||||
/** computes the functional interface method for a class, or returns null */
|
||||
private Method computeFunctionalInterfaceMethod(Struct clazz) {
|
||||
private PainlessMethod computeFunctionalInterfaceMethod(PainlessClass clazz) {
|
||||
if (!clazz.clazz.isInterface()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1166,7 +868,7 @@ public final class Definition {
|
|||
}
|
||||
// inspect the one method found from the reflection API, it should match the whitelist!
|
||||
java.lang.reflect.Method oneMethod = methods.get(0);
|
||||
Method painless = clazz.methods.get(new Definition.MethodKey(oneMethod.getName(), oneMethod.getParameterCount()));
|
||||
PainlessMethod painless = clazz.methods.get(new PainlessMethodKey(oneMethod.getName(), oneMethod.getParameterCount()));
|
||||
if (painless == null || painless.method.equals(org.objectweb.asm.commons.Method.getMethod(oneMethod)) == false) {
|
||||
throw new IllegalArgumentException("Class: " + clazz.name + " is functional but the functional " +
|
||||
"method is not whitelisted!");
|
||||
|
@ -1178,7 +880,7 @@ public final class Definition {
|
|||
return painlessTypesToJavaClasses.containsKey(painlessType);
|
||||
}
|
||||
|
||||
public Struct getPainlessStructFromJavaClass(Class<?> clazz) {
|
||||
public PainlessClass getPainlessStructFromJavaClass(Class<?> clazz) {
|
||||
return javaClassesToPainlessStructs.get(clazz);
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PainlessMethod {
|
||||
public final String name;
|
||||
public final PainlessClass owner;
|
||||
public final Class<?> augmentation;
|
||||
public final Class<?> rtn;
|
||||
public final List<Class<?>> arguments;
|
||||
public final org.objectweb.asm.commons.Method method;
|
||||
public final int modifiers;
|
||||
public final MethodHandle handle;
|
||||
|
||||
public PainlessMethod(String name, PainlessClass owner, Class<?> augmentation, Class<?> rtn, List<Class<?>> arguments,
|
||||
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
|
||||
this.name = name;
|
||||
this.augmentation = augmentation;
|
||||
this.owner = owner;
|
||||
this.rtn = rtn;
|
||||
this.arguments = Collections.unmodifiableList(arguments);
|
||||
this.method = method;
|
||||
this.modifiers = modifiers;
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns MethodType for this method.
|
||||
* <p>
|
||||
* This works even for user-defined Methods (where the MethodHandle is null).
|
||||
*/
|
||||
public MethodType getMethodType() {
|
||||
// we have a methodhandle already (e.g. whitelisted class)
|
||||
// just return its type
|
||||
if (handle != null) {
|
||||
return handle.type();
|
||||
}
|
||||
// otherwise compute it
|
||||
final Class<?> params[];
|
||||
final Class<?> returnValue;
|
||||
if (augmentation != null) {
|
||||
// static method disguised as virtual/interface method
|
||||
params = new Class<?>[1 + arguments.size()];
|
||||
params[0] = augmentation;
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
params[i + 1] = PainlessLookup.defClassToObjectClass(arguments.get(i));
|
||||
}
|
||||
returnValue = PainlessLookup.defClassToObjectClass(rtn);
|
||||
} else if (Modifier.isStatic(modifiers)) {
|
||||
// static method: straightforward copy
|
||||
params = new Class<?>[arguments.size()];
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
params[i] = PainlessLookup.defClassToObjectClass(arguments.get(i));
|
||||
}
|
||||
returnValue = PainlessLookup.defClassToObjectClass(rtn);
|
||||
} else if ("<init>".equals(name)) {
|
||||
// constructor: returns the owner class
|
||||
params = new Class<?>[arguments.size()];
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
params[i] = PainlessLookup.defClassToObjectClass(arguments.get(i));
|
||||
}
|
||||
returnValue = owner.clazz;
|
||||
} else {
|
||||
// virtual/interface method: add receiver class
|
||||
params = new Class<?>[1 + arguments.size()];
|
||||
params[0] = owner.clazz;
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
params[i + 1] = PainlessLookup.defClassToObjectClass(arguments.get(i));
|
||||
}
|
||||
returnValue = PainlessLookup.defClassToObjectClass(rtn);
|
||||
}
|
||||
return MethodType.methodType(returnValue, params);
|
||||
}
|
||||
|
||||
public void write(MethodWriter writer) {
|
||||
final org.objectweb.asm.Type type;
|
||||
final Class<?> clazz;
|
||||
if (augmentation != null) {
|
||||
assert Modifier.isStatic(modifiers);
|
||||
clazz = augmentation;
|
||||
type = org.objectweb.asm.Type.getType(augmentation);
|
||||
} else {
|
||||
clazz = owner.clazz;
|
||||
type = owner.type;
|
||||
}
|
||||
|
||||
if (Modifier.isStatic(modifiers)) {
|
||||
// invokeStatic assumes that the owner class is not an interface, so this is a
|
||||
// special case for interfaces where the interface method boolean needs to be set to
|
||||
// true to reference the appropriate class constant when calling a static interface
|
||||
// method since java 8 did not check, but java 9 and 10 do
|
||||
if (Modifier.isInterface(clazz.getModifiers())) {
|
||||
writer.visitMethodInsn(Opcodes.INVOKESTATIC,
|
||||
type.getInternalName(), name, getMethodType().toMethodDescriptorString(), true);
|
||||
} else {
|
||||
writer.invokeStatic(type, method);
|
||||
}
|
||||
} else if (Modifier.isInterface(clazz.getModifiers())) {
|
||||
writer.invokeInterface(type, method);
|
||||
} else {
|
||||
writer.invokeVirtual(type, method);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Key for looking up a method.
|
||||
* <p>
|
||||
* Methods are keyed on both name and arity, and can be overloaded once per arity.
|
||||
* This allows signatures such as {@code String.indexOf(String) vs String.indexOf(String, int)}.
|
||||
* <p>
|
||||
* It is less flexible than full signature overloading where types can differ too, but
|
||||
* better than just the name, and overloading types adds complexity to users, too.
|
||||
*/
|
||||
public final class PainlessMethodKey {
|
||||
public final String name;
|
||||
public final int arity;
|
||||
|
||||
/**
|
||||
* Create a new lookup key
|
||||
* @param name name of the method
|
||||
* @param arity number of parameters
|
||||
*/
|
||||
public PainlessMethodKey(String name, int arity) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.arity = arity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + arity;
|
||||
result = prime * result + name.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
PainlessMethodKey other = (PainlessMethodKey) obj;
|
||||
if (arity != other.arity) return false;
|
||||
if (!name.equals(other.name)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(name);
|
||||
sb.append('/');
|
||||
sb.append(arity);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -20,8 +20,8 @@
|
|||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessCast;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
||||
|
@ -118,7 +118,7 @@ public abstract class AExpression extends ANode {
|
|||
* @return The new child node for the parent node calling this method.
|
||||
*/
|
||||
AExpression cast(Locals locals) {
|
||||
Cast cast = AnalyzerCaster.getLegalCast(location, actual, expected, explicit, internal);
|
||||
PainlessCast cast = AnalyzerCaster.getLegalCast(location, actual, expected, explicit, internal);
|
||||
|
||||
if (cast == null) {
|
||||
if (constant == null || this instanceof EConstant) {
|
||||
|
@ -157,7 +157,7 @@ public abstract class AExpression extends ANode {
|
|||
|
||||
return ecast;
|
||||
} else {
|
||||
if (Definition.isConstantType(expected)) {
|
||||
if (PainlessLookup.isConstantType(expected)) {
|
||||
// For the case where a cast is required, a constant is set,
|
||||
// and the constant can be immediately cast to the expected type.
|
||||
// An EConstant replaces this node with the constant cast appropriately
|
||||
|
|
|
@ -22,8 +22,8 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.DefBootstrap;
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessCast;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -49,8 +49,8 @@ public final class EAssignment extends AExpression {
|
|||
private boolean cat = false;
|
||||
private Class<?> promote = null;
|
||||
private Class<?> shiftDistance; // for shifts, the RHS is promoted independently
|
||||
private Cast there = null;
|
||||
private Cast back = null;
|
||||
private PainlessCast there = null;
|
||||
private PainlessCast back = null;
|
||||
|
||||
public EAssignment(Location location, AExpression lhs, AExpression rhs, boolean pre, boolean post, Operation operation) {
|
||||
super(location);
|
||||
|
|
|
@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.DefBootstrap;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -106,7 +106,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply multiply [*] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote;
|
||||
|
@ -148,7 +148,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply divide [/] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote;
|
||||
|
@ -195,7 +195,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply remainder [%] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote;
|
||||
|
@ -242,7 +242,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply add [+] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote;
|
||||
|
@ -300,7 +300,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply subtract [-] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote;
|
||||
|
@ -358,7 +358,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (lhspromote == null || rhspromote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply left shift [<<] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote = lhspromote;
|
||||
|
@ -405,7 +405,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (lhspromote == null || rhspromote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply right shift [>>] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote = lhspromote;
|
||||
|
@ -455,7 +455,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (lhspromote == null || rhspromote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply unsigned shift [>>>] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
if (lhspromote == def.class || rhspromote == def.class) {
|
||||
|
@ -498,7 +498,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply and [&] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote;
|
||||
|
@ -537,7 +537,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply xor [^] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote;
|
||||
|
@ -577,7 +577,7 @@ public final class EBinary extends AExpression {
|
|||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply or [|] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
actual = promote;
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethodKey;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -40,7 +40,7 @@ public final class ECallLocal extends AExpression {
|
|||
private final String name;
|
||||
private final List<AExpression> arguments;
|
||||
|
||||
private Method method = null;
|
||||
private PainlessMethod method = null;
|
||||
|
||||
public ECallLocal(Location location, String name, List<AExpression> arguments) {
|
||||
super(location);
|
||||
|
@ -58,7 +58,7 @@ public final class ECallLocal extends AExpression {
|
|||
|
||||
@Override
|
||||
void analyze(Locals locals) {
|
||||
MethodKey methodKey = new MethodKey(name, arguments.size());
|
||||
PainlessMethodKey methodKey = new PainlessMethodKey(name, arguments.size());
|
||||
method = locals.getMethod(methodKey);
|
||||
|
||||
if (method == null) {
|
||||
|
|
|
@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.DefBootstrap;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.elasticsearch.painless.FunctionRef;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
|
@ -69,7 +69,7 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
|
|||
defPointer = "D" + variable + "." + call + ",1";
|
||||
} else {
|
||||
// typed implementation
|
||||
defPointer = "S" + Definition.ClassToName(captured.clazz) + "." + call + ",1";
|
||||
defPointer = "S" + PainlessLookup.ClassToName(captured.clazz) + "." + call + ",1";
|
||||
}
|
||||
actual = String.class;
|
||||
} else {
|
||||
|
@ -77,7 +77,7 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
|
|||
// static case
|
||||
if (captured.clazz != def.class) {
|
||||
try {
|
||||
ref = new FunctionRef(locals.getDefinition(), expected, Definition.ClassToName(captured.clazz), call, 1);
|
||||
ref = new FunctionRef(locals.getPainlessLookup(), expected, PainlessLookup.ClassToName(captured.clazz), call, 1);
|
||||
|
||||
// check casts between the interface method and the delegate method are legal
|
||||
for (int i = 0; i < ref.interfaceMethod.arguments.size(); ++i) {
|
||||
|
@ -109,7 +109,7 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
|
|||
// typed interface, dynamic implementation
|
||||
writer.visitVarInsn(MethodWriter.getType(captured.clazz).getOpcode(Opcodes.ILOAD), captured.getSlot());
|
||||
Type methodType = Type.getMethodType(MethodWriter.getType(expected), MethodWriter.getType(captured.clazz));
|
||||
writer.invokeDefCall(call, methodType, DefBootstrap.REFERENCE, Definition.ClassToName(expected));
|
||||
writer.invokeDefCall(call, methodType, DefBootstrap.REFERENCE, PainlessLookup.ClassToName(expected));
|
||||
} else {
|
||||
// typed interface, typed implementation
|
||||
writer.visitVarInsn(MethodWriter.getType(captured.clazz).getOpcode(Opcodes.ILOAD), captured.getSlot());
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessCast;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -35,9 +35,9 @@ import java.util.Set;
|
|||
final class ECast extends AExpression {
|
||||
|
||||
private AExpression child;
|
||||
private final Cast cast;
|
||||
private final PainlessCast cast;
|
||||
|
||||
ECast(Location location, AExpression child, Cast cast) {
|
||||
ECast(Location location, AExpression child, PainlessCast cast) {
|
||||
super(location);
|
||||
|
||||
this.child = Objects.requireNonNull(child);
|
||||
|
@ -63,6 +63,6 @@ final class ECast extends AExpression {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return singleLineToString(Definition.ClassToName(cast.to), child);
|
||||
return singleLineToString(PainlessLookup.ClassToName(cast.to), child);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.DefBootstrap;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -93,7 +93,7 @@ public final class EComp extends AExpression {
|
|||
|
||||
if (promotedType == null) {
|
||||
throw createError(new ClassCastException("Cannot apply equals [==] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
if (promotedType == def.class) {
|
||||
|
@ -142,7 +142,7 @@ public final class EComp extends AExpression {
|
|||
|
||||
if (promotedType == null) {
|
||||
throw createError(new ClassCastException("Cannot apply reference equals [===] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
left.expected = promotedType;
|
||||
|
@ -182,7 +182,7 @@ public final class EComp extends AExpression {
|
|||
|
||||
if (promotedType == null) {
|
||||
throw createError(new ClassCastException("Cannot apply not equals [!=] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
if (promotedType == def.class) {
|
||||
|
@ -231,7 +231,7 @@ public final class EComp extends AExpression {
|
|||
|
||||
if (promotedType == null) {
|
||||
throw createError(new ClassCastException("Cannot apply reference not equals [!==] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
left.expected = promotedType;
|
||||
|
@ -271,7 +271,7 @@ public final class EComp extends AExpression {
|
|||
|
||||
if (promotedType == null) {
|
||||
throw createError(new ClassCastException("Cannot apply greater than or equals [>=] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
if (promotedType == def.class) {
|
||||
|
@ -310,7 +310,7 @@ public final class EComp extends AExpression {
|
|||
|
||||
if (promotedType == null) {
|
||||
throw createError(new ClassCastException("Cannot apply greater than [>] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
if (promotedType == def.class) {
|
||||
|
@ -349,7 +349,7 @@ public final class EComp extends AExpression {
|
|||
|
||||
if (promotedType == null) {
|
||||
throw createError(new ClassCastException("Cannot apply less than or equals [<=] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
if (promotedType == def.class) {
|
||||
|
@ -388,7 +388,7 @@ public final class EComp extends AExpression {
|
|||
|
||||
if (promotedType == null) {
|
||||
throw createError(new ClassCastException("Cannot apply less than [>=] to types " +
|
||||
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
|
||||
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
|
||||
}
|
||||
|
||||
if (promotedType == def.class) {
|
||||
|
|
|
@ -50,7 +50,7 @@ public final class EExplicit extends AExpression {
|
|||
@Override
|
||||
void analyze(Locals locals) {
|
||||
try {
|
||||
actual = locals.getDefinition().getJavaClassFromPainlessType(type);
|
||||
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethodKey;
|
||||
import org.elasticsearch.painless.FunctionRef;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
|
@ -66,15 +66,15 @@ public final class EFunctionRef extends AExpression implements ILambda {
|
|||
try {
|
||||
if ("this".equals(type)) {
|
||||
// user's own function
|
||||
Method interfaceMethod = locals.getDefinition().getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
PainlessMethod interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + Definition.ClassToName(expected) + "], not a functional interface");
|
||||
"to [" + PainlessLookup.ClassToName(expected) + "], not a functional interface");
|
||||
}
|
||||
Method delegateMethod = locals.getMethod(new MethodKey(call, interfaceMethod.arguments.size()));
|
||||
PainlessMethod delegateMethod = locals.getMethod(new PainlessMethodKey(call, interfaceMethod.arguments.size()));
|
||||
if (delegateMethod == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + Definition.ClassToName(expected) + "], function not found");
|
||||
"to [" + PainlessLookup.ClassToName(expected) + "], function not found");
|
||||
}
|
||||
ref = new FunctionRef(expected, interfaceMethod, delegateMethod, 0);
|
||||
|
||||
|
@ -90,7 +90,7 @@ public final class EFunctionRef extends AExpression implements ILambda {
|
|||
}
|
||||
} else {
|
||||
// whitelist lookup
|
||||
ref = new FunctionRef(locals.getDefinition(), expected, type, call, 0);
|
||||
ref = new FunctionRef(locals.getPainlessLookup(), expected, type, call, 0);
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -58,13 +58,13 @@ public final class EInstanceof extends AExpression {
|
|||
|
||||
// ensure the specified type is part of the definition
|
||||
try {
|
||||
clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
|
||||
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
// map to wrapped type for primitive types
|
||||
resolvedType = clazz.isPrimitive() ? Definition.getBoxedType(clazz) : Definition.defClassToObjectClass(clazz);
|
||||
resolvedType = clazz.isPrimitive() ? PainlessLookup.getBoxedType(clazz) : PainlessLookup.defClassToObjectClass(clazz);
|
||||
|
||||
// analyze and cast the expression
|
||||
expression.analyze(locals);
|
||||
|
@ -75,7 +75,7 @@ public final class EInstanceof extends AExpression {
|
|||
primitiveExpression = expression.actual.isPrimitive();
|
||||
// map to wrapped type for primitive types
|
||||
expressionType = expression.actual.isPrimitive() ?
|
||||
Definition.getBoxedType(expression.actual) : Definition.defClassToObjectClass(clazz);
|
||||
PainlessLookup.getBoxedType(expression.actual) : PainlessLookup.defClassToObjectClass(clazz);
|
||||
|
||||
actual = boolean.class;
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.elasticsearch.painless.FunctionRef;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
|
@ -103,7 +103,7 @@ public final class ELambda extends AExpression implements ILambda {
|
|||
void analyze(Locals locals) {
|
||||
Class<?> returnType;
|
||||
List<String> actualParamTypeStrs;
|
||||
Method interfaceMethod;
|
||||
PainlessMethod interfaceMethod;
|
||||
// inspect the target first, set interface method if we know it.
|
||||
if (expected == null) {
|
||||
interfaceMethod = null;
|
||||
|
@ -120,15 +120,15 @@ public final class ELambda extends AExpression implements ILambda {
|
|||
}
|
||||
} else {
|
||||
// we know the method statically, infer return type and any unknown/def types
|
||||
interfaceMethod = locals.getDefinition().getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw createError(new IllegalArgumentException("Cannot pass lambda to [" + Definition.ClassToName(expected) +
|
||||
throw createError(new IllegalArgumentException("Cannot pass lambda to [" + PainlessLookup.ClassToName(expected) +
|
||||
"], not a functional interface"));
|
||||
}
|
||||
// check arity before we manipulate parameters
|
||||
if (interfaceMethod.arguments.size() != paramTypeStrs.size())
|
||||
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name +
|
||||
"] in [" + Definition.ClassToName(expected) + "]");
|
||||
"] in [" + PainlessLookup.ClassToName(expected) + "]");
|
||||
// for method invocation, its allowed to ignore the return value
|
||||
if (interfaceMethod.rtn == void.class) {
|
||||
returnType = def.class;
|
||||
|
@ -140,7 +140,7 @@ public final class ELambda extends AExpression implements ILambda {
|
|||
for (int i = 0; i < paramTypeStrs.size(); i++) {
|
||||
String paramType = paramTypeStrs.get(i);
|
||||
if (paramType == null) {
|
||||
actualParamTypeStrs.add(Definition.ClassToName(interfaceMethod.arguments.get(i)));
|
||||
actualParamTypeStrs.add(PainlessLookup.ClassToName(interfaceMethod.arguments.get(i)));
|
||||
} else {
|
||||
actualParamTypeStrs.add(paramType);
|
||||
}
|
||||
|
@ -162,16 +162,16 @@ public final class ELambda extends AExpression implements ILambda {
|
|||
List<String> paramTypes = new ArrayList<>(captures.size() + actualParamTypeStrs.size());
|
||||
List<String> paramNames = new ArrayList<>(captures.size() + paramNameStrs.size());
|
||||
for (Variable var : captures) {
|
||||
paramTypes.add(Definition.ClassToName(var.clazz));
|
||||
paramTypes.add(PainlessLookup.ClassToName(var.clazz));
|
||||
paramNames.add(var.name);
|
||||
}
|
||||
paramTypes.addAll(actualParamTypeStrs);
|
||||
paramNames.addAll(paramNameStrs);
|
||||
|
||||
// desugar lambda body into a synthetic method
|
||||
desugared = new SFunction(reserved, location, Definition.ClassToName(returnType), name,
|
||||
desugared = new SFunction(reserved, location, PainlessLookup.ClassToName(returnType), name,
|
||||
paramTypes, paramNames, statements, true);
|
||||
desugared.generateSignature(locals.getDefinition());
|
||||
desugared.generateSignature(locals.getPainlessLookup());
|
||||
desugared.analyze(Locals.newLambdaScope(locals.getProgramScope(), returnType,
|
||||
desugared.parameters, captures.size(), reserved.getMaxLoopCounter()));
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethodKey;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -37,8 +37,8 @@ import java.util.Set;
|
|||
public final class EListInit extends AExpression {
|
||||
private final List<AExpression> values;
|
||||
|
||||
private Method constructor = null;
|
||||
private Method method = null;
|
||||
private PainlessMethod constructor = null;
|
||||
private PainlessMethod method = null;
|
||||
|
||||
public EListInit(Location location, List<AExpression> values) {
|
||||
super(location);
|
||||
|
@ -61,13 +61,14 @@ public final class EListInit extends AExpression {
|
|||
|
||||
actual = ArrayList.class;
|
||||
|
||||
constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("<init>", 0));
|
||||
constructor =
|
||||
locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(new PainlessMethodKey("<init>", 0));
|
||||
|
||||
if (constructor == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
|
||||
method = locals.getDefinition().getPainlessStructFromJavaClass(actual).methods.get(new MethodKey("add", 1));
|
||||
method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods.get(new PainlessMethodKey("add", 1));
|
||||
|
||||
if (method == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethodKey;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -38,8 +38,8 @@ public final class EMapInit extends AExpression {
|
|||
private final List<AExpression> keys;
|
||||
private final List<AExpression> values;
|
||||
|
||||
private Method constructor = null;
|
||||
private Method method = null;
|
||||
private PainlessMethod constructor = null;
|
||||
private PainlessMethod method = null;
|
||||
|
||||
public EMapInit(Location location, List<AExpression> keys, List<AExpression> values) {
|
||||
super(location);
|
||||
|
@ -67,13 +67,14 @@ public final class EMapInit extends AExpression {
|
|||
|
||||
actual = HashMap.class;
|
||||
|
||||
constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("<init>", 0));
|
||||
constructor =
|
||||
locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(new PainlessMethodKey("<init>", 0));
|
||||
|
||||
if (constructor == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
}
|
||||
|
||||
method = locals.getDefinition().getPainlessStructFromJavaClass(actual).methods.get(new MethodKey("put", 2));
|
||||
method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods.get(new PainlessMethodKey("put", 2));
|
||||
|
||||
if (method == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
|
|
@ -61,7 +61,7 @@ public final class ENewArray extends AExpression {
|
|||
Class<?> clazz;
|
||||
|
||||
try {
|
||||
clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
|
||||
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethodKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -39,7 +39,7 @@ public final class ENewObj extends AExpression {
|
|||
private final String type;
|
||||
private final List<AExpression> arguments;
|
||||
|
||||
private Method constructor;
|
||||
private PainlessMethod constructor;
|
||||
|
||||
public ENewObj(Location location, String type, List<AExpression> arguments) {
|
||||
super(location);
|
||||
|
@ -58,13 +58,13 @@ public final class ENewObj extends AExpression {
|
|||
@Override
|
||||
void analyze(Locals locals) {
|
||||
try {
|
||||
actual = locals.getDefinition().getJavaClassFromPainlessType(this.type);
|
||||
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
Struct struct = locals.getDefinition().getPainlessStructFromJavaClass(actual);
|
||||
constructor = struct.constructors.get(new Definition.MethodKey("<init>", arguments.size()));
|
||||
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual);
|
||||
constructor = struct.constructors.get(new PainlessMethodKey("<init>", arguments.size()));
|
||||
|
||||
if (constructor != null) {
|
||||
Class<?>[] types = new Class<?>[constructor.arguments.size()];
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless.node;
|
||||
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -53,7 +53,7 @@ public final class ENull extends AExpression {
|
|||
if (expected != null) {
|
||||
if (expected.isPrimitive()) {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"Cannot cast null to a primitive type [" + Definition.ClassToName(expected) + "]."));
|
||||
"Cannot cast null to a primitive type [" + PainlessLookup.ClassToName(expected) + "]."));
|
||||
}
|
||||
|
||||
actual = expected;
|
||||
|
|
|
@ -48,7 +48,7 @@ public final class EStatic extends AExpression {
|
|||
@Override
|
||||
void analyze(Locals locals) {
|
||||
try {
|
||||
actual = locals.getDefinition().getJavaClassFromPainlessType(type);
|
||||
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.AnalyzerCaster;
|
||||
import org.elasticsearch.painless.DefBootstrap;
|
||||
import org.elasticsearch.painless.Definition;
|
||||
import org.elasticsearch.painless.Definition.def;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup.def;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
|
@ -93,7 +93,7 @@ public final class EUnary extends AExpression {
|
|||
promote = AnalyzerCaster.promoteNumeric(child.actual, false);
|
||||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply not [~] to type [" + Definition.ClassToName(child.actual) + "]."));
|
||||
throw createError(new ClassCastException("Cannot apply not [~] to type [" + PainlessLookup.ClassToName(child.actual) + "]."));
|
||||
}
|
||||
|
||||
child.expected = promote;
|
||||
|
@ -122,7 +122,8 @@ public final class EUnary extends AExpression {
|
|||
promote = AnalyzerCaster.promoteNumeric(child.actual, true);
|
||||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply positive [+] to type [" + Definition.ClassToName(child.actual) + "]."));
|
||||
throw createError(
|
||||
new ClassCastException("Cannot apply positive [+] to type [" + PainlessLookup.ClassToName(child.actual) + "]."));
|
||||
}
|
||||
|
||||
child.expected = promote;
|
||||
|
@ -155,7 +156,8 @@ public final class EUnary extends AExpression {
|
|||
promote = AnalyzerCaster.promoteNumeric(child.actual, true);
|
||||
|
||||
if (promote == null) {
|
||||
throw createError(new ClassCastException("Cannot apply negative [-] to type [" + Definition.ClassToName(child.actual) + "]."));
|
||||
throw createError(
|
||||
new ClassCastException("Cannot apply negative [-] to type [" + PainlessLookup.ClassToName(child.actual) + "]."));
|
||||
}
|
||||
|
||||
child.expected = promote;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue