mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3540029931 | |||
| eafe3afe11 | |||
| 26e0343c16 | |||
| 4d5e7207f2 | |||
| 430fc25aca | |||
| 0882ca57d4 | |||
| 94bbf85c0e | |||
| 2c2ef12f68 | |||
| 120d512ff6 | |||
| 3ddf183922 | |||
| 4c69bfd32f | |||
| 41d71e9a7f | |||
| 36682b7ad2 | |||
| 30601a5014 | |||
| f2371f5e7d | |||
| f0da099b12 | |||
| d00fd4c502 | |||
| 44a6d13cc0 | |||
| 284657355a | |||
| 6a81ed3a50 | |||
| 0a053cfccb | |||
| 22bb76d326 | |||
| 4dc45d551c | |||
| e5d05ddfc3 | |||
| 44a5f8ec06 | |||
| 0a8bda40f4 | |||
| 8a879c6fed | |||
| 28d11aaf64 | |||
| 02941127e1 | |||
| b1b24458c9 | |||
| 854b625be2 | |||
| a2b7a907ec | |||
| 4f0a8911ca | |||
| 40fef7b232 | |||
| 423134f64c | |||
| 574c10d219 | |||
| 2b4b947050 | |||
| 4993b1090a | |||
| 3adabf391f | |||
| 0026338c00 | |||
| cdea667e58 | |||
| 66b1c0b4b0 | |||
| 6e42d36614 | |||
| 7f2c93fa1f | |||
| 4c351e811a | |||
| 5b67dea506 | |||
| b9fe095f60 | |||
| 6b17014b5a | |||
| 878246b09b | |||
| 9067ccab2d | |||
| 4e00aece7a | |||
| c9841f37b6 | |||
| 92d513e9fb | |||
| 782ee34cb7 | |||
| 80c10fad92 | |||
| 498a0d286f | |||
| e9d45d1b49 | |||
| 0851b0f72d | |||
| 91d991e86c | |||
| 960f6fb936 | |||
| 4d3d34528e | |||
| 78ba946266 | |||
| 42e7318cbb | |||
| 000383fbff | |||
| 37cbdc2cf4 | |||
| 6d95e7f9d9 | |||
| 45a7917a36 | |||
| 25b95b8b98 | |||
| 6bd7a7321e | |||
| 11aaba9877 | |||
| 07fd7606e7 | |||
| 40d2466334 | |||
| 3d6d853bbc | |||
| 90ea39cc6b | |||
| 21c3d4f4a9 | |||
| de53d77344 | |||
| e4e90bbec0 | |||
| d0828be0cd | |||
| 4a4b332709 | |||
| 33f1c9b614 | |||
| 53ac812fb0 | |||
| 60c89dd2df | |||
| 4bd2531774 | |||
| 4fd5630700 | |||
| 26cd33cb2b | |||
| 2ee393ae71 | |||
| 0815d29e45 | |||
| 777f01d786 | |||
| f8fea013fc | |||
| 916539178b | |||
| fdf3bcc9d9 | |||
| 5ddc984192 | |||
| b2ce54e7f1 | |||
| 937ab5f4b2 | |||
| 064c618050 | |||
| 8c80ec1138 | |||
| 912fa7602a | |||
| 525fc7a27e | |||
| 72285034ba | |||
| 933474000b | |||
| 4e8aebcde7 | |||
| dff5b1ff8e | |||
| fa0a4a0a46 | |||
| 41a6b7ec20 | |||
| cfaa4ba89c | |||
| 598e9d586b | |||
| 750a8b359f | |||
| c838bcff3a | |||
| 109b00d24d | |||
| b6abf45a56 | |||
| 2d94b43faa | |||
| f7f73439ab | |||
| 5e55558a93 | |||
| eee50d3b8a | |||
| 92af390ed2 | |||
| 332d2a36bb | |||
| 92b2b828f5 | |||
| a11222f681 | |||
| 7de8d81932 | |||
| 0e33537d9d | |||
| 00da70e26b | |||
| 72b44cebea | |||
| 1ee3777ac7 | |||
| cad6e8756d | |||
| c7bd685c54 | |||
| 4f472d2ac4 | |||
| a18842b72b | |||
| a41f97bc2b |
@@ -28,6 +28,7 @@ buildSrc/build
|
||||
/integration-tests/build
|
||||
/src/asciidoc/build
|
||||
target/
|
||||
/target/
|
||||
|
||||
# Eclipse artifacts, including WTP generated manifests
|
||||
.classpath
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=8.0.352-librca
|
||||
java=8.0.362-librca
|
||||
|
||||
+16
-16
@@ -28,18 +28,18 @@ configure(allprojects) { project ->
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
|
||||
mavenBom "io.netty:netty-bom:4.1.85.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.25"
|
||||
mavenBom "io.netty:netty-bom:4.1.90.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.30"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.3"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.49.v20220914"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.51.v20230217"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
|
||||
mavenBom "org.junit:junit-bom:5.8.2"
|
||||
}
|
||||
dependencies {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.19.0') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.20.0') {
|
||||
entry 'log4j-api'
|
||||
entry 'log4j-core'
|
||||
entry 'log4j-jul'
|
||||
@@ -92,7 +92,7 @@ configure(allprojects) { project ->
|
||||
entry 'jibx-run'
|
||||
}
|
||||
dependency "org.ogce:xpp3:1.1.6"
|
||||
dependency "org.yaml:snakeyaml:1.30"
|
||||
dependency "org.yaml:snakeyaml:1.33"
|
||||
|
||||
dependency "com.h2database:h2:2.1.214"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
|
||||
@@ -128,18 +128,18 @@ configure(allprojects) { project ->
|
||||
dependency "org.webjars:webjars-locator-core:0.48"
|
||||
dependency "org.webjars:underscorejs:1.8.3"
|
||||
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.68') {
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.73') {
|
||||
entry 'tomcat-util'
|
||||
entry('tomcat-websocket') {
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.68') {
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.73') {
|
||||
entry 'tomcat-embed-core'
|
||||
entry 'tomcat-embed-websocket'
|
||||
}
|
||||
dependencySet(group: 'io.undertow', version: '2.2.21.Final') {
|
||||
dependencySet(group: 'io.undertow', version: '2.2.23.Final') {
|
||||
entry 'undertow-core'
|
||||
entry('undertow-servlet') {
|
||||
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
|
||||
@@ -191,14 +191,14 @@ configure(allprojects) { project ->
|
||||
dependency "org.junit.support:testng-engine:1.0.4"
|
||||
dependency "org.hamcrest:hamcrest:2.2"
|
||||
dependency "org.awaitility:awaitility:3.1.6"
|
||||
dependency "org.assertj:assertj-core:3.23.0"
|
||||
dependency "org.assertj:assertj-core:3.24.2"
|
||||
dependencySet(group: 'org.xmlunit', version: '2.9.0') {
|
||||
entry 'xmlunit-assertj'
|
||||
entry('xmlunit-matchers') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.mockito', version: '4.8.1') {
|
||||
dependencySet(group: 'org.mockito', version: '4.9.0') { // spring-beans tests fail with 4.10+
|
||||
entry('mockito-core') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
@@ -206,10 +206,10 @@ configure(allprojects) { project ->
|
||||
}
|
||||
dependency "io.mockk:mockk:1.12.1"
|
||||
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.66.0") {
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.70.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.66.0") {
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.70.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
|
||||
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.4"
|
||||
toolVersion = "10.9.1"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -377,9 +377,9 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
|
||||
"https://www.ehcache.org/apidocs/2.10.4/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://fasterxml.github.io/jackson-core/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.12.7/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.2/",
|
||||
|
||||
@@ -5,7 +5,6 @@ ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java8
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java8
|
||||
ENV JDK11 /opt/openjdk/java11
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
|
||||
@@ -5,15 +5,9 @@ case "$1" in
|
||||
java8)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/8u345+1/bellsoft-jdk8u345+1-linux-amd64.tar.gz"
|
||||
;;
|
||||
java11)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/11.0.16+8/bellsoft-jdk11.0.16+8-linux-amd64.tar.gz"
|
||||
;;
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.4+8/bellsoft-jdk17.0.4+8-linux-amd64.tar.gz"
|
||||
;;
|
||||
java18)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.2+10/bellsoft-jdk18.0.2+10-linux-amd64.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
exit 1
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java8 java11 java17
|
||||
for jdk in java8 java17
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
|
||||
@@ -8,7 +8,4 @@ milestone: "5.3.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
registry-mirror-host: docker.repo.spring.io
|
||||
registry-mirror-username: ((artifactory-username))
|
||||
registry-mirror-password: ((artifactory-password))
|
||||
task-timeout: 1h00m
|
||||
|
||||
+13
-47
@@ -23,11 +23,6 @@ anchors:
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: ((milestone))
|
||||
registry-mirror-vars: ®istry-mirror-vars
|
||||
registry-mirror-host: ((registry-mirror-host))
|
||||
registry-mirror-username: ((registry-mirror-username))
|
||||
registry-mirror-password: ((registry-mirror-password))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
@@ -48,31 +43,37 @@ resource_types:
|
||||
- name: registry-image
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.5.0
|
||||
tag: 1.7.1
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.17
|
||||
tag: 0.0.18
|
||||
- name: github-release
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.5.5
|
||||
tag: 1.8.0
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: pull-request
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: teliaoss/github-pr-resource
|
||||
tag: v0.23.0
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
@@ -101,6 +102,7 @@ resources:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
@@ -125,14 +127,6 @@ resources:
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk11-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk11-build
|
||||
- name: repo-status-jdk17-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
@@ -176,7 +170,7 @@ jobs:
|
||||
image: ci-image
|
||||
vars:
|
||||
ci-image-name: ci-image
|
||||
<<: *registry-mirror-vars
|
||||
<<: *docker-resource-source
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
@@ -237,34 +231,6 @@ jobs:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk11-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 11
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: jdk17-build
|
||||
serial: true
|
||||
public: true
|
||||
@@ -282,7 +248,7 @@ jobs:
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 15
|
||||
TEST_TOOLCHAIN: 17
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
@@ -471,7 +437,7 @@ jobs:
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk11-build", "jdk17-build"]
|
||||
jobs: ["build", "jdk17-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
|
||||
@@ -4,6 +4,6 @@ set -e
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 \
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
|
||||
@@ -4,11 +4,9 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
tag: 0.9.1
|
||||
registry_mirror:
|
||||
host: ((registry-mirror-host))
|
||||
username: ((registry-mirror-username))
|
||||
password: ((registry-mirror-password))
|
||||
tag: 0.10.0
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: ci-images-git-repo
|
||||
outputs:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.24-SNAPSHOT
|
||||
version=5.3.26
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -81,6 +81,10 @@ plugins.withType(JavaPlugin) {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
if(testLanguageVersion == JavaLanguageVersion.of(17)) {
|
||||
jvmArgs(["--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.util=ALL-UNNAMED"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,21 +134,6 @@ pluginManager.withPlugin("kotlin") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (testToolchainConfigured()) {
|
||||
def testLanguageVersion = testToolchainLanguageVersion()
|
||||
def compiler = javaToolchains.compilerFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
|
||||
def javaVersion = testLanguageVersion.toString() == '8' ? '1.8' : testLanguageVersion.toString()
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.11.1"
|
||||
id "io.spring.ge.conventions" version "0.0.11"
|
||||
id "com.gradle.enterprise" version "3.12.1"
|
||||
id "io.spring.ge.conventions" version "0.0.13"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
|
||||
+3
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,10 +23,6 @@ import org.springframework.lang.Nullable;
|
||||
* Subinterface of {@link org.springframework.aop.aspectj.AspectInstanceFactory}
|
||||
* that returns {@link AspectMetadata} associated with AspectJ-annotated classes.
|
||||
*
|
||||
* <p>Ideally, AspectInstanceFactory would include this method itself, but because
|
||||
* AspectMetadata uses Java-5-only {@link org.aspectj.lang.reflect.AjType},
|
||||
* we need to split out this subinterface.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see AspectMetadata
|
||||
@@ -35,13 +31,13 @@ import org.springframework.lang.Nullable;
|
||||
public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory {
|
||||
|
||||
/**
|
||||
* Return the AspectJ AspectMetadata for this factory's aspect.
|
||||
* Get the AspectJ AspectMetadata for this factory's aspect.
|
||||
* @return the aspect metadata
|
||||
*/
|
||||
AspectMetadata getAspectMetadata();
|
||||
|
||||
/**
|
||||
* Return the best possible creation mutex for this factory.
|
||||
* Get the best possible creation mutex for this factory.
|
||||
* @return the mutex object (may be {@code null} for no mutex to use)
|
||||
* @since 4.3
|
||||
*/
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Various {@link org.springframework.aop.framework.autoproxy.TargetSourceCreator}
|
||||
* implementations for use with Spring's AOP auto-proxying support.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.aop.framework.autoproxy.target;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,7 +62,7 @@ import org.springframework.util.StringUtils;
|
||||
* <p>Mainly for internal use within the framework, but to some degree also
|
||||
* useful for application classes. Consider
|
||||
* <a href="https://commons.apache.org/proper/commons-beanutils/">Apache Commons BeanUtils</a>,
|
||||
* <a href="https://hotelsdotcom.github.io/bull/">BULL - Bean Utils Light Library</a>,
|
||||
* <a href="https://github.com/ExpediaGroup/bull">BULL - Bean Utils Light Library</a>,
|
||||
* or similar third-party frameworks for more comprehensive bean utilities.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
@@ -247,7 +247,8 @@ public abstract class BeanUtils {
|
||||
// A single public constructor
|
||||
return (Constructor<T>) ctors[0];
|
||||
}
|
||||
else if (ctors.length == 0){
|
||||
else if (ctors.length == 0) {
|
||||
// No public constructors -> check non-public
|
||||
ctors = clazz.getDeclaredConstructors();
|
||||
if (ctors.length == 1) {
|
||||
// A single non-public constructor, e.g. from a non-public record type
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import java.beans.BeanDescriptor;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
@@ -51,6 +52,10 @@ public class SimpleBeanInfoFactory implements BeanInfoFactory, Ordered {
|
||||
PropertyDescriptorUtils.determineBasicProperties(beanClass);
|
||||
|
||||
return new SimpleBeanInfo() {
|
||||
@Override
|
||||
public BeanDescriptor getBeanDescriptor() {
|
||||
return new BeanDescriptor(beanClass);
|
||||
}
|
||||
@Override
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return pds.toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,6 +25,7 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
@@ -240,10 +241,11 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
}
|
||||
}
|
||||
if (targetAnnotation == null) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
// Look for matching annotation on the target class
|
||||
if (getBeanFactory() != null) {
|
||||
if (beanFactory != null) {
|
||||
try {
|
||||
Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
|
||||
Class<?> beanType = beanFactory.getType(bdHolder.getBeanName());
|
||||
if (beanType != null) {
|
||||
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -184,8 +184,9 @@ public abstract class YamlProcessor {
|
||||
protected Yaml createYaml() {
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setAllowDuplicateKeys(false);
|
||||
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
|
||||
new DumperOptions(), loaderOptions);
|
||||
DumperOptions dumperOptions = new DumperOptions();
|
||||
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(dumperOptions),
|
||||
dumperOptions, loaderOptions);
|
||||
}
|
||||
|
||||
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+33
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -214,12 +214,15 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,12 +243,15 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,18 +326,23 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
||||
this.beanName + "' threw an exception";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex.getTargetException());
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex.getTargetException());
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
||||
this.beanName + "' threw an exception";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex.getTargetException());
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex.getTargetException());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
|
||||
"' on bean with name '" + this.beanName + "'", ex);
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
|
||||
"' on bean with name '" + this.beanName + "'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -547,7 +547,8 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
|
||||
* @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader)
|
||||
*/
|
||||
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
|
||||
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
|
||||
ResourceLoader resourceLoader = getResourceLoader();
|
||||
ClassLoader cl = (resourceLoader != null ? resourceLoader.getClassLoader() : getBeanClassLoader());
|
||||
return new DefaultNamespaceHandlerResolver(cl);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -77,6 +77,21 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceWrappedInstance() {
|
||||
GetterBean target = new GetterBean();
|
||||
BeanWrapperImpl accessor = createAccessor(target);
|
||||
accessor.setPropertyValue("name", "tom");
|
||||
assertThat(target.getAliasedName()).isEqualTo("tom");
|
||||
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
|
||||
|
||||
target = new GetterBean();
|
||||
accessor.setWrappedInstance(target);
|
||||
accessor.setPropertyValue("name", "tom");
|
||||
assertThat(target.getAliasedName()).isEqualTo("tom");
|
||||
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setValidAndInvalidPropertyValuesShouldContainExceptionDetails() {
|
||||
TestBean target = new TestBean();
|
||||
@@ -380,6 +395,7 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("try")
|
||||
public static class ActiveResource implements AutoCloseable {
|
||||
|
||||
public ActiveResource getResource() {
|
||||
|
||||
+1
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -436,7 +436,6 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
@Test
|
||||
void empty() {
|
||||
ListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
assertThat(lbf.getBeanDefinitionNames() != null).as("No beans defined --> array != null").isTrue();
|
||||
assertThat(lbf.getBeanDefinitionNames().length == 0).as("No beans defined after no arg constructor").isTrue();
|
||||
assertThat(lbf.getBeanDefinitionCount() == 0).as("No beans defined after no arg constructor").isTrue();
|
||||
@@ -778,21 +777,6 @@ class DefaultListableBeanFactoryTests {
|
||||
assertThat(factory.getType("child")).isEqualTo(DerivedTestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameAlreadyBound() {
|
||||
Properties p = new Properties();
|
||||
p.setProperty("kerry.(class)", TestBean.class.getName());
|
||||
p.setProperty("kerry.age", "35");
|
||||
registerBeanDefinitions(p);
|
||||
try {
|
||||
registerBeanDefinitions(p);
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
assertThat(ex.getBeanName()).isEqualTo("kerry");
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
private void singleTestBean(ListableBeanFactory lbf) {
|
||||
assertThat(lbf.getBeanDefinitionCount() == 1).as("1 beans defined").isTrue();
|
||||
String[] names = lbf.getBeanDefinitionNames();
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -148,7 +148,7 @@ public class QualifierAnnotationAutowireBeanFactoryTests {
|
||||
lbf.registerBeanDefinition(MARK, person2);
|
||||
MethodParameter param = new MethodParameter(QualifiedTestBean.class.getDeclaredConstructor(Person.class), 0);
|
||||
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(param, false);
|
||||
param.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||
param.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
assertThat(param.getParameterName()).isEqualTo("tpb");
|
||||
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
|
||||
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isTrue();
|
||||
@@ -174,9 +174,9 @@ public class QualifierAnnotationAutowireBeanFactoryTests {
|
||||
new MethodParameter(QualifiedTestBean.class.getDeclaredMethod("autowireNonqualified", Person.class), 0);
|
||||
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(qualifiedParam, false);
|
||||
DependencyDescriptor nonqualifiedDescriptor = new DependencyDescriptor(nonqualifiedParam, false);
|
||||
qualifiedParam.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||
qualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
assertThat(qualifiedParam.getParameterName()).isEqualTo("tpb");
|
||||
nonqualifiedParam.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||
nonqualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
assertThat(nonqualifiedParam.getParameterName()).isEqualTo("tpb");
|
||||
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
|
||||
assertThat(lbf.isAutowireCandidate(JUERGEN, nonqualifiedDescriptor)).isTrue();
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -502,7 +502,7 @@ class CustomEditorTests {
|
||||
CharBean cb = new CharBean();
|
||||
BeanWrapper bw = new BeanWrapperImpl(cb);
|
||||
|
||||
bw.setPropertyValue("myChar", new Character('c'));
|
||||
bw.setPropertyValue("myChar", 'c');
|
||||
assertThat(cb.getMyChar()).isEqualTo('c');
|
||||
|
||||
bw.setPropertyValue("myChar", "c");
|
||||
@@ -526,16 +526,16 @@ class CustomEditorTests {
|
||||
bw.registerCustomEditor(Character.class, new CharacterEditor(true));
|
||||
|
||||
bw.setPropertyValue("myCharacter", 'c');
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('c'));
|
||||
assertThat(cb.getMyCharacter()).isEqualTo('c');
|
||||
|
||||
bw.setPropertyValue("myCharacter", "c");
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('c'));
|
||||
assertThat(cb.getMyCharacter()).isEqualTo('c');
|
||||
|
||||
bw.setPropertyValue("myCharacter", "\u0041");
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('A'));
|
||||
assertThat(cb.getMyCharacter()).isEqualTo('A');
|
||||
|
||||
bw.setPropertyValue("myCharacter", " ");
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf(' '));
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(' ');
|
||||
|
||||
bw.setPropertyValue("myCharacter", "");
|
||||
assertThat(cb.getMyCharacter()).isNull();
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
Vendored
+9
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,6 +51,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @author Brian Clozel
|
||||
* @since 4.3
|
||||
* @see CaffeineCache
|
||||
*/
|
||||
@@ -188,8 +189,13 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
@Override
|
||||
@Nullable
|
||||
public Cache getCache(String name) {
|
||||
return this.cacheMap.computeIfAbsent(name, cacheName ->
|
||||
this.dynamic ? createCaffeineCache(cacheName) : null);
|
||||
if (this.dynamic) {
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
return (cache != null) ? cache : this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
|
||||
}
|
||||
else {
|
||||
return this.cacheMap.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -132,10 +132,10 @@ public class JavaMailSenderImpl implements JavaMailSender {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow {code Map} access to the JavaMail properties of this sender,
|
||||
* Allow {@code Map} access to the JavaMail properties of this sender,
|
||||
* with the option to add or override specific entries.
|
||||
* <p>Useful for specifying entries directly, for example via
|
||||
* {code javaMailProperties[mail.smtp.auth]}.
|
||||
* {@code javaMailProperties[mail.smtp.auth]}.
|
||||
*/
|
||||
public Properties getJavaMailProperties() {
|
||||
return this.javaMailProperties;
|
||||
|
||||
+6
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -729,9 +729,11 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
/**
|
||||
* Modify the application context's internal bean factory after its standard
|
||||
* initialization. All bean definitions will have been loaded, but no beans
|
||||
* will have been instantiated yet. This allows for registering special
|
||||
* BeanPostProcessors etc in certain ApplicationContext implementations.
|
||||
* initialization. The initial definition resources will have been loaded but no
|
||||
* post-processors will have run and no derived bean definitions will have been
|
||||
* registered, and most importantly, no beans will have been instantiated yet.
|
||||
* <p>This template method allows for registering special BeanPostProcessors
|
||||
* etc in certain AbstractApplicationContext subclasses.
|
||||
* @param beanFactory the bean factory used by the application context
|
||||
*/
|
||||
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,6 @@ import java.beans.PropertyDescriptor;
|
||||
|
||||
import javax.management.DynamicMBean;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
import javax.management.ObjectName;
|
||||
@@ -32,6 +31,7 @@ import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.jmx.IJmxTestBean;
|
||||
import org.springframework.jmx.JmxTestBean;
|
||||
import org.springframework.jmx.export.TestDynamicMBean;
|
||||
import org.springframework.util.MBeanTestUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class JmxUtilsTests {
|
||||
|
||||
@@ -131,10 +132,11 @@ class JmxUtilsTests {
|
||||
MBeanServer server = null;
|
||||
try {
|
||||
server = JmxUtils.locateMBeanServer();
|
||||
assertThat(server).isNotNull();
|
||||
}
|
||||
finally {
|
||||
if (server != null) {
|
||||
MBeanServerFactory.releaseMBeanServer(server);
|
||||
MBeanTestUtils.releaseMBeanServer(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,21 +21,25 @@ import java.lang.reflect.Field;
|
||||
import javax.naming.spi.NamingManager;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledForJreRange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.condition.JRE.JAVA_16;
|
||||
|
||||
/**
|
||||
* Tests for {@link JndiLocatorDelegate}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class JndiLocatorDelegateTests {
|
||||
@DisabledForJreRange(
|
||||
min = JAVA_16,
|
||||
disabledReason = "Cannot use reflection to set private static field in javax.naming.spi.NamingManager")
|
||||
class JndiLocatorDelegateTests {
|
||||
|
||||
@Test
|
||||
public void isDefaultJndiEnvironmentAvailableFalse() throws Exception {
|
||||
void isDefaultJndiEnvironmentAvailableFalse() throws Exception {
|
||||
Field builderField = NamingManager.class.getDeclaredField("initctx_factory_builder");
|
||||
builderField.setAccessible(true);
|
||||
Object oldBuilder = builderField.get(null);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,37 +18,59 @@ package org.springframework.util;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
||||
/**
|
||||
* Utilities for MBean tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class MBeanTestUtils {
|
||||
|
||||
/**
|
||||
* Resets MBeanServerFactory and ManagementFactory to a known consistent state.
|
||||
* <p>This involves releasing all currently registered MBeanServers and resetting
|
||||
* the platformMBeanServer to null.
|
||||
* Reset the {@link MBeanServerFactory} to a known consistent state. This involves
|
||||
* {@linkplain #releaseMBeanServer(MBeanServer) releasing} all currently registered
|
||||
* MBeanServers.
|
||||
* <p>On JDK 8 - JDK 16, this method also resets the platformMBeanServer field
|
||||
* in {@link ManagementFactory} to {@code null}.
|
||||
*/
|
||||
public static synchronized void resetMBeanServers() throws Exception {
|
||||
for (MBeanServer server : MBeanServerFactory.findMBeanServer(null)) {
|
||||
try {
|
||||
MBeanServerFactory.releaseMBeanServer(server);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (!ex.getMessage().contains("not in list")) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
releaseMBeanServer(server);
|
||||
}
|
||||
|
||||
Field field = ManagementFactory.class.getDeclaredField("platformMBeanServer");
|
||||
field.setAccessible(true);
|
||||
field.set(null, null);
|
||||
if (!isCurrentJreWithinRange(JRE.JAVA_16, JRE.OTHER)) {
|
||||
Field field = ManagementFactory.class.getDeclaredField("platformMBeanServer");
|
||||
field.setAccessible(true);
|
||||
field.set(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to release the supplied {@link MBeanServer}.
|
||||
* <p>Ignores any {@link IllegalArgumentException} thrown by
|
||||
* {@link MBeanServerFactory#releaseMBeanServer(MBeanServer)} whose error
|
||||
* message contains the text "not in list".
|
||||
*/
|
||||
public static void releaseMBeanServer(MBeanServer server) {
|
||||
try {
|
||||
MBeanServerFactory.releaseMBeanServer(server);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (!ex.getMessage().contains("not in list")) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isCurrentJreWithinRange(JRE min, JRE max) {
|
||||
return EnumSet.range(min, max).contains(JRE.currentVersion());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,6 +82,17 @@ public abstract class AnnotationVisitor {
|
||||
this.av = annotationVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The annotation visitor to which this visitor must delegate method calls. May be {@literal
|
||||
* null}.
|
||||
*
|
||||
* @return the annotation visitor to which this visitor must delegate method calls, or {@literal
|
||||
* null}.
|
||||
*/
|
||||
public AnnotationVisitor getDelegate() {
|
||||
return av;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a primitive value of the annotation.
|
||||
*
|
||||
|
||||
@@ -194,7 +194,7 @@ public class ClassReader {
|
||||
this.b = classFileBuffer;
|
||||
// Check the class' major_version. This field is after the magic and minor_version fields, which
|
||||
// use 4 and 2 bytes respectively.
|
||||
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V19) {
|
||||
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V21) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported class file major version " + readShort(classFileOffset + 6));
|
||||
}
|
||||
@@ -308,6 +308,7 @@ public class ClassReader {
|
||||
* @return the content of the given input stream.
|
||||
* @throws IOException if a problem occurs during reading.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UseTryWithResources")
|
||||
private static byte[] readStream(final InputStream inputStream, final boolean close)
|
||||
throws IOException {
|
||||
if (inputStream == null) {
|
||||
@@ -376,7 +377,7 @@ public class ClassReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal of name of the super class (see {@link Type#getInternalName()}). For
|
||||
* Returns the internal name of the super class (see {@link Type#getInternalName()}). For
|
||||
* interfaces, the super class is {@link Object}.
|
||||
*
|
||||
* @return the internal name of the super class, or {@literal null} for {@link Object} class.
|
||||
|
||||
@@ -42,7 +42,8 @@ public final class ClassTooLargeException extends IndexOutOfBoundsException {
|
||||
/**
|
||||
* Constructs a new {@link ClassTooLargeException}.
|
||||
*
|
||||
* @param className the internal name of the class.
|
||||
* @param className the internal name of the class (see {@link
|
||||
* org.objectweb.asm.Type#getInternalName()}).
|
||||
* @param constantPoolCount the number of constant pool items of the class.
|
||||
*/
|
||||
public ClassTooLargeException(final String className, final int constantPoolCount) {
|
||||
@@ -52,7 +53,7 @@ public final class ClassTooLargeException extends IndexOutOfBoundsException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal name of the class.
|
||||
* Returns the internal name of the class (see {@link org.objectweb.asm.Type#getInternalName()}).
|
||||
*
|
||||
* @return the internal name of the class.
|
||||
*/
|
||||
|
||||
@@ -81,6 +81,15 @@ public abstract class ClassVisitor {
|
||||
this.cv = classVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The class visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*
|
||||
* @return the class visitor to which this visitor must delegate method calls, or {@literal null}.
|
||||
*/
|
||||
public ClassVisitor getDelegate() {
|
||||
return cv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the header of the class.
|
||||
*
|
||||
@@ -155,7 +164,8 @@ public abstract class ClassVisitor {
|
||||
* implicitly its own nest, so it's invalid to call this method with the visited class name as
|
||||
* argument.
|
||||
*
|
||||
* @param nestHost the internal name of the host class of the nest.
|
||||
* @param nestHost the internal name of the host class of the nest (see {@link
|
||||
* Type#getInternalName()}).
|
||||
*/
|
||||
public void visitNestHost(final String nestHost) {
|
||||
if (api < Opcodes.ASM7) {
|
||||
@@ -167,14 +177,19 @@ public abstract class ClassVisitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the enclosing class of the class. This method must be called only if the class has an
|
||||
* enclosing class.
|
||||
* Visits the enclosing class of the class. This method must be called only if this class is a
|
||||
* local or anonymous class. See the JVMS 4.7.7 section for more details.
|
||||
*
|
||||
* @param owner internal name of the enclosing class of the class.
|
||||
* @param owner internal name of the enclosing class of the class (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param name the name of the method that contains the class, or {@literal null} if the class is
|
||||
* not enclosed in a method of its enclosing class.
|
||||
* not enclosed in a method or constructor of its enclosing class (e.g. if it is enclosed in
|
||||
* an instance initializer, static initializer, instance variable initializer, or class
|
||||
* variable initializer).
|
||||
* @param descriptor the descriptor of the method that contains the class, or {@literal null} if
|
||||
* the class is not enclosed in a method of its enclosing class.
|
||||
* the class is not enclosed in a method or constructor of its enclosing class (e.g. if it is
|
||||
* enclosed in an instance initializer, static initializer, instance variable initializer, or
|
||||
* class variable initializer).
|
||||
*/
|
||||
public void visitOuterClass(final String owner, final String name, final String descriptor) {
|
||||
if (cv != null) {
|
||||
@@ -241,7 +256,7 @@ public abstract class ClassVisitor {
|
||||
* the visited class is the host of a nest. A nest host is implicitly a member of its own nest, so
|
||||
* it's invalid to call this method with the visited class name as argument.
|
||||
*
|
||||
* @param nestMember the internal name of a nest member.
|
||||
* @param nestMember the internal name of a nest member (see {@link Type#getInternalName()}).
|
||||
*/
|
||||
public void visitNestMember(final String nestMember) {
|
||||
if (api < Opcodes.ASM7) {
|
||||
@@ -256,7 +271,8 @@ public abstract class ClassVisitor {
|
||||
* Visits a permitted subclasses. A permitted subclass is one of the allowed subclasses of the
|
||||
* current class.
|
||||
*
|
||||
* @param permittedSubclass the internal name of a permitted subclass.
|
||||
* @param permittedSubclass the internal name of a permitted subclass (see {@link
|
||||
* Type#getInternalName()}).
|
||||
*/
|
||||
public void visitPermittedSubclass(final String permittedSubclass) {
|
||||
if (api < Opcodes.ASM9) {
|
||||
@@ -269,15 +285,18 @@ public abstract class ClassVisitor {
|
||||
|
||||
/**
|
||||
* Visits information about an inner class. This inner class is not necessarily a member of the
|
||||
* class being visited.
|
||||
* class being visited. More precisely, every class or interface C which is referenced by this
|
||||
* class and which is not a package member must be visited with this method. This class must
|
||||
* reference its nested class or interface members, and its enclosing class, if any. See the JVMS
|
||||
* 4.7.6 section for more details.
|
||||
*
|
||||
* @param name the internal name of an inner class (see {@link Type#getInternalName()}).
|
||||
* @param outerName the internal name of the class to which the inner class belongs (see {@link
|
||||
* Type#getInternalName()}). May be {@literal null} for not member classes.
|
||||
* @param innerName the (simple) name of the inner class inside its enclosing class. May be
|
||||
* {@literal null} for anonymous inner classes.
|
||||
* @param access the access flags of the inner class as originally declared in the enclosing
|
||||
* class.
|
||||
* @param name the internal name of C (see {@link Type#getInternalName()}).
|
||||
* @param outerName the internal name of the class or interface C is a member of (see {@link
|
||||
* Type#getInternalName()}). Must be {@literal null} if C is not the member of a class or
|
||||
* interface (e.g. for local or anonymous classes).
|
||||
* @param innerName the (simple) name of C. Must be {@literal null} for anonymous inner classes.
|
||||
* @param access the access flags of C originally declared in the source code from which this
|
||||
* class was compiled.
|
||||
*/
|
||||
public void visitInnerClass(
|
||||
final String name, final String outerName, final String innerName, final int access) {
|
||||
|
||||
@@ -842,7 +842,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||||
* sub classes, and is normally not needed by class generators or adapters.</i>
|
||||
*
|
||||
* @param value the internal name of the class.
|
||||
* @param value the internal name of the class (see {@link Type#getInternalName()}).
|
||||
* @return the index of a new or already existing class reference item.
|
||||
*/
|
||||
public int newClass(final String value) {
|
||||
@@ -894,7 +894,8 @@ public class ClassWriter extends ClassVisitor {
|
||||
* Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
|
||||
* Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
|
||||
* {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
|
||||
* @param owner the internal name of the field or method owner class.
|
||||
* @param owner the internal name of the field or method owner class (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param name the name of the field or method.
|
||||
* @param descriptor the descriptor of the field or method.
|
||||
* @return the index of a new or already existing method type reference item.
|
||||
@@ -916,7 +917,8 @@ public class ClassWriter extends ClassVisitor {
|
||||
* Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
|
||||
* Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
|
||||
* {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
|
||||
* @param owner the internal name of the field or method owner class.
|
||||
* @param owner the internal name of the field or method owner class (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param name the name of the field or method.
|
||||
* @param descriptor the descriptor of the field or method.
|
||||
* @param isInterface true if the owner is an interface.
|
||||
@@ -978,7 +980,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||||
* sub classes, and is normally not needed by class generators or adapters.</i>
|
||||
*
|
||||
* @param owner the internal name of the field's owner class.
|
||||
* @param owner the internal name of the field's owner class (see {@link Type#getInternalName()}).
|
||||
* @param name the field's name.
|
||||
* @param descriptor the field's descriptor.
|
||||
* @return the index of a new or already existing field reference item.
|
||||
@@ -992,7 +994,8 @@ public class ClassWriter extends ClassVisitor {
|
||||
* constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||||
* sub classes, and is normally not needed by class generators or adapters.</i>
|
||||
*
|
||||
* @param owner the internal name of the method's owner class.
|
||||
* @param owner the internal name of the method's owner class (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param name the method's name.
|
||||
* @param descriptor the method's descriptor.
|
||||
* @param isInterface {@literal true} if {@code owner} is an interface.
|
||||
@@ -1028,9 +1031,10 @@ public class ClassWriter extends ClassVisitor {
|
||||
* currently being generated by this ClassWriter, which can of course not be loaded since it is
|
||||
* under construction.
|
||||
*
|
||||
* @param type1 the internal name of a class.
|
||||
* @param type2 the internal name of another class.
|
||||
* @return the internal name of the common super class of the two given classes.
|
||||
* @param type1 the internal name of a class (see {@link Type#getInternalName()}).
|
||||
* @param type2 the internal name of another class (see {@link Type#getInternalName()}).
|
||||
* @return the internal name of the common super class of the two given classes (see {@link
|
||||
* Type#getInternalName()}).
|
||||
*/
|
||||
protected String getCommonSuperClass(final String type1, final String type2) {
|
||||
ClassLoader classLoader = getClassLoader();
|
||||
|
||||
@@ -78,6 +78,15 @@ public abstract class FieldVisitor {
|
||||
this.fv = fieldVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The field visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*
|
||||
* @return the field visitor to which this visitor must delegate method calls, or {@literal null}.
|
||||
*/
|
||||
public FieldVisitor getDelegate() {
|
||||
return fv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits an annotation of the field.
|
||||
*
|
||||
|
||||
@@ -367,11 +367,12 @@ class Frame {
|
||||
typeValue = REFERENCE_KIND | symbolTable.addType(internalName);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid descriptor fragment: " + buffer.substring(elementDescriptorOffset));
|
||||
}
|
||||
return ((elementDescriptorOffset - offset) << DIM_SHIFT) | typeValue;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
throw new IllegalArgumentException("Invalid descriptor: " + buffer.substring(offset));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public final class Handle {
|
||||
* {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link
|
||||
* Opcodes#H_INVOKEINTERFACE}.
|
||||
* @param owner the internal name of the class that owns the field or method designated by this
|
||||
* handle.
|
||||
* handle (see {@link Type#getInternalName()}).
|
||||
* @param name the name of the field or method designated by this handle.
|
||||
* @param descriptor the descriptor of the field or method designated by this handle.
|
||||
* @deprecated this constructor has been superseded by {@link #Handle(int, String, String, String,
|
||||
@@ -85,7 +85,7 @@ public final class Handle {
|
||||
* {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link
|
||||
* Opcodes#H_INVOKEINTERFACE}.
|
||||
* @param owner the internal name of the class that owns the field or method designated by this
|
||||
* handle.
|
||||
* handle (see {@link Type#getInternalName()}).
|
||||
* @param name the name of the field or method designated by this handle.
|
||||
* @param descriptor the descriptor of the field or method designated by this handle.
|
||||
* @param isInterface whether the owner is an interface or not.
|
||||
@@ -118,7 +118,8 @@ public final class Handle {
|
||||
/**
|
||||
* Returns the internal name of the class that owns the field or method designated by this handle.
|
||||
*
|
||||
* @return the internal name of the class that owns the field or method designated by this handle.
|
||||
* @return the internal name of the class that owns the field or method designated by this handle
|
||||
* (see {@link Type#getInternalName()}).
|
||||
*/
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class MethodTooLargeException extends IndexOutOfBoundsException {
|
||||
/**
|
||||
* Constructs a new {@link MethodTooLargeException}.
|
||||
*
|
||||
* @param className the internal name of the owner class.
|
||||
* @param className the internal name of the owner class (see {@link Type#getInternalName()}).
|
||||
* @param methodName the name of the method.
|
||||
* @param descriptor the descriptor of the method.
|
||||
* @param codeSize the size of the method's Code attribute, in bytes.
|
||||
@@ -64,7 +64,7 @@ public final class MethodTooLargeException extends IndexOutOfBoundsException {
|
||||
/**
|
||||
* Returns the internal name of the owner class.
|
||||
*
|
||||
* @return the internal name of the owner class.
|
||||
* @return the internal name of the owner class (see {@link Type#getInternalName()}).
|
||||
*/
|
||||
public String getClassName() {
|
||||
return className;
|
||||
|
||||
@@ -94,6 +94,16 @@ public abstract class MethodVisitor {
|
||||
this.mv = methodVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*
|
||||
* @return the method visitor to which this visitor must delegate method calls, or {@literal
|
||||
* null}.
|
||||
*/
|
||||
public MethodVisitor getDelegate() {
|
||||
return mv;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// Parameters, annotations and non standard attributes
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
@@ -120,7 +130,7 @@ public abstract class MethodVisitor {
|
||||
* @return a visitor to the visit the actual default value of this annotation interface method, or
|
||||
* {@literal null} if this visitor is not interested in visiting this default value. The
|
||||
* 'name' parameters passed to the methods of this annotation visitor are ignored. Moreover,
|
||||
* exacly one visit method must be called on this annotation visitor, followed by visitEnd.
|
||||
* exactly one visit method must be called on this annotation visitor, followed by visitEnd.
|
||||
*/
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
if (mv != null) {
|
||||
@@ -273,15 +283,17 @@ public abstract class MethodVisitor {
|
||||
* @param type the type of this stack map frame. Must be {@link Opcodes#F_NEW} for expanded
|
||||
* frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link
|
||||
* Opcodes#F_SAME} or {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed frames.
|
||||
* @param numLocal the number of local variables in the visited frame.
|
||||
* @param numLocal the number of local variables in the visited frame. Long and double values
|
||||
* count for one variable.
|
||||
* @param local the local variable types in this frame. This array must not be modified. Primitive
|
||||
* types are represented by {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link
|
||||
* Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL} or
|
||||
* {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a single element).
|
||||
* Reference types are represented by String objects (representing internal names), and
|
||||
* uninitialized types by Label objects (this label designates the NEW instruction that
|
||||
* created this uninitialized value).
|
||||
* @param numStack the number of operand stack elements in the visited frame.
|
||||
* Reference types are represented by String objects (representing internal names, see {@link
|
||||
* Type#getInternalName()}), and uninitialized types by Label objects (this label designates
|
||||
* the NEW instruction that created this uninitialized value).
|
||||
* @param numStack the number of operand stack elements in the visited frame. Long and double
|
||||
* values count for one stack element.
|
||||
* @param stack the operand stack types in this frame. This array must not be modified. Its
|
||||
* content has the same format as the "local" array.
|
||||
* @throws IllegalStateException if a frame is visited just after another one, without any
|
||||
@@ -360,7 +372,7 @@ public abstract class MethodVisitor {
|
||||
|
||||
/**
|
||||
* Visits a type instruction. A type instruction is an instruction that takes the internal name of
|
||||
* a class as parameter.
|
||||
* a class as parameter (see {@link Type#getInternalName()}).
|
||||
*
|
||||
* @param opcode the opcode of the type instruction to be visited. This opcode is either NEW,
|
||||
* ANEWARRAY, CHECKCAST or INSTANCEOF.
|
||||
@@ -552,12 +564,12 @@ public abstract class MethodVisitor {
|
||||
/**
|
||||
* Visits an IINC instruction.
|
||||
*
|
||||
* @param var index of the local variable to be incremented.
|
||||
* @param varIndex index of the local variable to be incremented.
|
||||
* @param increment amount to increment the local variable by.
|
||||
*/
|
||||
public void visitIincInsn(final int var, final int increment) {
|
||||
public void visitIincInsn(final int varIndex, final int increment) {
|
||||
if (mv != null) {
|
||||
mv.visitIincInsn(var, increment);
|
||||
mv.visitIincInsn(varIndex, increment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,8 +655,9 @@ public abstract class MethodVisitor {
|
||||
* @param start the beginning of the exception handler's scope (inclusive).
|
||||
* @param end the end of the exception handler's scope (exclusive).
|
||||
* @param handler the beginning of the exception handler's code.
|
||||
* @param type the internal name of the type of exceptions handled by the handler, or {@literal
|
||||
* null} to catch any exceptions (for "finally" blocks).
|
||||
* @param type the internal name of the type of exceptions handled by the handler (see {@link
|
||||
* Type#getInternalName()}), or {@literal null} to catch any exceptions (for "finally"
|
||||
* blocks).
|
||||
* @throws IllegalArgumentException if one of the labels has already been visited by this visitor
|
||||
* (by the {@link #visitLabel} method).
|
||||
*/
|
||||
|
||||
@@ -284,6 +284,8 @@ public interface Opcodes {
|
||||
int V17 = 0 << 16 | 61;
|
||||
int V18 = 0 << 16 | 62;
|
||||
int V19 = 0 << 16 | 63;
|
||||
int V20 = 0 << 16 | 64;
|
||||
int V21 = 0 << 16 | 65;
|
||||
|
||||
/**
|
||||
* Version flag indicating that the class is using 'preview' features.
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class RecordComponentVisitor {
|
||||
/**
|
||||
* The record visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*/
|
||||
/*package-private*/ RecordComponentVisitor delegate;
|
||||
protected RecordComponentVisitor delegate;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link RecordComponentVisitor}.
|
||||
@@ -83,7 +83,8 @@ public abstract class RecordComponentVisitor {
|
||||
/**
|
||||
* The record visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*
|
||||
* @return the record visitor to which this visitor must delegate method calls or {@literal null}.
|
||||
* @return the record visitor to which this visitor must delegate method calls, or {@literal
|
||||
* null}.
|
||||
*/
|
||||
public RecordComponentVisitor getDelegate() {
|
||||
return delegate;
|
||||
|
||||
@@ -37,7 +37,7 @@ final class RecordComponentWriter extends RecordComponentVisitor {
|
||||
/** The name_index field of the Record attribute. */
|
||||
private final int nameIndex;
|
||||
|
||||
/** The descriptor_index field of the the Record attribute. */
|
||||
/** The descriptor_index field of the Record attribute. */
|
||||
private final int descriptorIndex;
|
||||
|
||||
/**
|
||||
|
||||
@@ -245,7 +245,7 @@ public final class Type {
|
||||
/**
|
||||
* Returns the {@link Type} corresponding to the given internal name.
|
||||
*
|
||||
* @param internalName an internal name.
|
||||
* @param internalName an internal name (see {@link Type#getInternalName()}).
|
||||
* @return the {@link Type} corresponding to the given internal name.
|
||||
*/
|
||||
public static Type getObjectType(final String internalName) {
|
||||
@@ -708,8 +708,8 @@ public final class Type {
|
||||
*
|
||||
* @return the size of the arguments of the method (plus one for the implicit this argument),
|
||||
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||
*/
|
||||
public int getArgumentsAndReturnSizes() {
|
||||
return getArgumentsAndReturnSizes(getDescriptor());
|
||||
@@ -721,8 +721,8 @@ public final class Type {
|
||||
* @param methodDescriptor a method descriptor.
|
||||
* @return the size of the arguments of the method (plus one for the implicit this argument),
|
||||
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||
*/
|
||||
public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
|
||||
int argumentsSize = 1;
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,9 +18,9 @@ package org.springframework.core;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link ParameterNameDiscoverer} strategy interface,
|
||||
* using the Java 8 standard reflection mechanism (if available), and falling back
|
||||
* to the ASM-based {@link LocalVariableTableParameterNameDiscoverer} for checking
|
||||
* debug information in the class file.
|
||||
* using the Java 8 standard reflection mechanism, and falling back to the ASM-based
|
||||
* {@link LocalVariableTableParameterNameDiscoverer} for checking debug information
|
||||
* in the class file (e.g. for classes compiled with earlier Java versions).
|
||||
*
|
||||
* <p>If a Kotlin reflection implementation is present,
|
||||
* {@link KotlinReflectionParameterNameDiscoverer} is added first in the list and
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,11 +31,13 @@ import org.springframework.lang.Nullable;
|
||||
* {@link ParameterNameDiscoverer} implementation which uses Kotlin's reflection facilities
|
||||
* for introspecting parameter names.
|
||||
*
|
||||
* Compared to {@link StandardReflectionParameterNameDiscoverer}, it allows in addition to
|
||||
* <p>Compared to {@link StandardReflectionParameterNameDiscoverer}, it allows in addition to
|
||||
* determine interface parameter names without requiring Java 8 -parameters compiler flag.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
* @see StandardReflectionParameterNameDiscoverer
|
||||
* @see DefaultParameterNameDiscoverer
|
||||
*/
|
||||
public class KotlinReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
|
||||
|
||||
|
||||
+6
@@ -47,12 +47,18 @@ import org.springframework.util.ClassUtils;
|
||||
* caches the ASM discovered information for each introspected Class, in a thread-safe
|
||||
* manner. It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
|
||||
*
|
||||
* <p>This discoverer variant is effectively superseded by the Java 8 based
|
||||
* {@link StandardReflectionParameterNameDiscoverer} but included as a fallback still
|
||||
* (for code not compiled with the standard "-parameters" compiler flag).
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @see StandardReflectionParameterNameDiscoverer
|
||||
* @see DefaultParameterNameDiscoverer
|
||||
*/
|
||||
public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {
|
||||
|
||||
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,10 +26,15 @@ import org.springframework.lang.Nullable;
|
||||
* {@link ParameterNameDiscoverer} implementation which uses JDK 8's reflection facilities
|
||||
* for introspecting parameter names (based on the "-parameters" compiler flag).
|
||||
*
|
||||
* <p>This is a key element of {@link DefaultParameterNameDiscoverer} where it is being
|
||||
* combined with {@link KotlinReflectionParameterNameDiscoverer} if Kotlin is present.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see java.lang.reflect.Method#getParameters()
|
||||
* @see java.lang.reflect.Parameter#getName()
|
||||
* @see KotlinReflectionParameterNameDiscoverer
|
||||
* @see DefaultParameterNameDiscoverer
|
||||
*/
|
||||
public class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
|
||||
|
||||
|
||||
@@ -86,17 +86,6 @@ final class AttributeMethods {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if this instance only contains a single attribute named
|
||||
* {@code value}.
|
||||
* @return {@code true} if there is only a value attribute
|
||||
*/
|
||||
boolean hasOnlyValueAttribute() {
|
||||
return (this.attributeMethods.length == 1 &&
|
||||
MergedAnnotation.VALUE.equals(this.attributeMethods[0].getName()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if values from the given annotation can be safely accessed without
|
||||
* causing any {@link TypeNotPresentException TypeNotPresentExceptions}.
|
||||
|
||||
+2
-2
@@ -166,8 +166,8 @@ public abstract class RepeatableContainers {
|
||||
|
||||
private static Object computeRepeatedAnnotationsMethod(Class<? extends Annotation> annotationType) {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(annotationType);
|
||||
if (methods.hasOnlyValueAttribute()) {
|
||||
Method method = methods.get(0);
|
||||
Method method = methods.get(MergedAnnotation.VALUE);
|
||||
if (method != null) {
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (returnType.isArray()) {
|
||||
Class<?> componentType = returnType.getComponentType();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,12 +22,13 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple log message type for use with Commons Logging, allowing
|
||||
* for convenient lazy resolution of a given {@link Supplier} instance
|
||||
* (typically bound to a Java 8 lambda expression) or a printf-style
|
||||
* format string ({@link String#format}) in its {@link #toString()}.
|
||||
* A simple log message type for use with Commons Logging, allowing for convenient
|
||||
* lazy resolution of a given {@link Supplier} instance (typically bound to a lambda
|
||||
* expression) or a printf-style format string ({@link String#format}) in its
|
||||
* {@link #toString()}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.2
|
||||
* @see #of(Supplier)
|
||||
* @see #format(String, Object)
|
||||
@@ -77,7 +78,7 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
/**
|
||||
* Build a lazily resolving message from the given supplier.
|
||||
* @param supplier the supplier (typically bound to a Java 8 lambda expression)
|
||||
* @param supplier the supplier (typically bound to a lambda expression)
|
||||
* @see #toString()
|
||||
*/
|
||||
public static LogMessage of(Supplier<? extends CharSequence> supplier) {
|
||||
@@ -87,63 +88,68 @@ public abstract class LogMessage implements CharSequence {
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and argument.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the argument
|
||||
* @param arg1 the argument (can be {@code null})
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object arg1) {
|
||||
public static LogMessage format(String format, @Nullable Object arg1) {
|
||||
return new FormatMessage1(format, arg1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @param arg1 the first argument (can be {@code null})
|
||||
* @param arg2 the second argument (can be {@code null})
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object arg1, Object arg2) {
|
||||
public static LogMessage format(String format, @Nullable Object arg1, @Nullable Object arg2) {
|
||||
return new FormatMessage2(format, arg1, arg2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @param arg3 the third argument
|
||||
* @param arg1 the first argument (can be {@code null})
|
||||
* @param arg2 the second argument (can be {@code null})
|
||||
* @param arg3 the third argument (can be {@code null})
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object arg1, Object arg2, Object arg3) {
|
||||
public static LogMessage format(String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) {
|
||||
return new FormatMessage3(format, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @param arg3 the third argument
|
||||
* @param arg4 the fourth argument
|
||||
* @param arg1 the first argument (can be {@code null})
|
||||
* @param arg2 the second argument (can be {@code null})
|
||||
* @param arg3 the third argument (can be {@code null})
|
||||
* @param arg4 the fourth argument (can be {@code null})
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
|
||||
public static LogMessage format(String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3,
|
||||
@Nullable Object arg4) {
|
||||
return new FormatMessage4(format, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and varargs.
|
||||
* <p>This varargs {@code format()} variant may be costly. You should therefore
|
||||
* use the individual argument variants whenever possible:
|
||||
* {@link #format(String, Object)}, {@link #format(String, Object, Object)}, etc.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param args the varargs array (costly, prefer individual arguments)
|
||||
* @param args the varargs array (can be {@code null} and can contain {@code null}
|
||||
* elements)
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object... args) {
|
||||
public static LogMessage format(String format, @Nullable Object... args) {
|
||||
return new FormatMessageX(format, args);
|
||||
}
|
||||
|
||||
|
||||
private static final class SupplierMessage extends LogMessage {
|
||||
|
||||
private Supplier<? extends CharSequence> supplier;
|
||||
private final Supplier<? extends CharSequence> supplier;
|
||||
|
||||
SupplierMessage(Supplier<? extends CharSequence> supplier) {
|
||||
Assert.notNull(supplier, "Supplier must not be null");
|
||||
@@ -170,9 +176,10 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessage1 extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object arg1;
|
||||
|
||||
FormatMessage1(String format, Object arg1) {
|
||||
FormatMessage1(String format, @Nullable Object arg1) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
}
|
||||
@@ -186,11 +193,13 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessage2 extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object arg1;
|
||||
|
||||
@Nullable
|
||||
private final Object arg2;
|
||||
|
||||
FormatMessage2(String format, Object arg1, Object arg2) {
|
||||
FormatMessage2(String format, @Nullable Object arg1, @Nullable Object arg2) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
@@ -205,13 +214,16 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessage3 extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object arg1;
|
||||
|
||||
@Nullable
|
||||
private final Object arg2;
|
||||
|
||||
@Nullable
|
||||
private final Object arg3;
|
||||
|
||||
FormatMessage3(String format, Object arg1, Object arg2, Object arg3) {
|
||||
FormatMessage3(String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
@@ -227,15 +239,20 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessage4 extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object arg1;
|
||||
|
||||
@Nullable
|
||||
private final Object arg2;
|
||||
|
||||
@Nullable
|
||||
private final Object arg3;
|
||||
|
||||
@Nullable
|
||||
private final Object arg4;
|
||||
|
||||
FormatMessage4(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
|
||||
FormatMessage4(String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3,
|
||||
@Nullable Object arg4) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
@@ -252,9 +269,10 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessageX extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object[] args;
|
||||
|
||||
FormatMessageX(String format, Object... args) {
|
||||
FormatMessageX(String format, @Nullable Object... args) {
|
||||
super(format);
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -90,4 +90,9 @@ public class ExecutorServiceAdapter extends AbstractExecutorService {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @Override on JDK 19
|
||||
public void close() {
|
||||
// no-op in order to avoid container-triggered shutdown call which would lead to exception logging
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -64,7 +64,7 @@ public class InstanceFilter<T> {
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the specified {code instance} matches this filter.
|
||||
* Determine if the specified {@code instance} matches this filter.
|
||||
*/
|
||||
public boolean match(T instance) {
|
||||
Assert.notNull(instance, "Instance to match must not be null");
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework;
|
||||
|
||||
import org.junit.platform.suite.api.IncludeClassNamePatterns;
|
||||
import org.junit.platform.suite.api.SelectPackages;
|
||||
import org.junit.platform.suite.api.Suite;
|
||||
|
||||
/**
|
||||
* JUnit Platform based test suite for tests in the spring-core module.
|
||||
*
|
||||
* <p><strong>This suite is only intended to be used manually within an IDE.</strong>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
@Suite
|
||||
@SelectPackages({"org.springframework.core", "org.springframework.util"})
|
||||
@IncludeClassNamePatterns(".*Tests?$")
|
||||
class SpringCoreTestSuite {
|
||||
}
|
||||
+9
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
class LocalVariableTableParameterNameDiscovererTests {
|
||||
|
||||
private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
private final ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
|
||||
@Test
|
||||
@@ -162,23 +162,23 @@ class LocalVariableTableParameterNameDiscovererTests {
|
||||
|
||||
Constructor<?> ctor = clazz.getDeclaredConstructor(Object.class);
|
||||
String[] names = discoverer.getParameterNames(ctor);
|
||||
assertThat(names.length).isEqualTo(1);
|
||||
assertThat(names).hasSize(1);
|
||||
assertThat(names[0]).isEqualTo("key");
|
||||
|
||||
ctor = clazz.getDeclaredConstructor(Object.class, Object.class);
|
||||
names = discoverer.getParameterNames(ctor);
|
||||
assertThat(names.length).isEqualTo(2);
|
||||
assertThat(names).hasSize(2);
|
||||
assertThat(names[0]).isEqualTo("key");
|
||||
assertThat(names[1]).isEqualTo("value");
|
||||
|
||||
Method m = clazz.getMethod("generifiedStaticMethod", Object.class);
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names.length).isEqualTo(1);
|
||||
assertThat(names).hasSize(1);
|
||||
assertThat(names[0]).isEqualTo("param");
|
||||
|
||||
m = clazz.getMethod("generifiedMethod", Object.class, long.class, Object.class, Object.class);
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names.length).isEqualTo(4);
|
||||
assertThat(names).hasSize(4);
|
||||
assertThat(names[0]).isEqualTo("param");
|
||||
assertThat(names[1]).isEqualTo("x");
|
||||
assertThat(names[2]).isEqualTo("key");
|
||||
@@ -186,21 +186,21 @@ class LocalVariableTableParameterNameDiscovererTests {
|
||||
|
||||
m = clazz.getMethod("voidStaticMethod", Object.class, long.class, int.class);
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names.length).isEqualTo(3);
|
||||
assertThat(names).hasSize(3);
|
||||
assertThat(names[0]).isEqualTo("obj");
|
||||
assertThat(names[1]).isEqualTo("x");
|
||||
assertThat(names[2]).isEqualTo("i");
|
||||
|
||||
m = clazz.getMethod("nonVoidStaticMethod", Object.class, long.class, int.class);
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names.length).isEqualTo(3);
|
||||
assertThat(names).hasSize(3);
|
||||
assertThat(names[0]).isEqualTo("obj");
|
||||
assertThat(names[1]).isEqualTo("x");
|
||||
assertThat(names[2]).isEqualTo("i");
|
||||
|
||||
m = clazz.getMethod("getDate");
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names.length).isEqualTo(0);
|
||||
assertThat(names).isEmpty();
|
||||
}
|
||||
|
||||
@Disabled("Ignored because Ubuntu packages OpenJDK with debug symbols enabled. See SPR-8078.")
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -43,11 +43,10 @@ import static org.junit.jupiter.api.condition.JRE.JAVA_14;
|
||||
@DisabledForJreRange(min = JAVA_14)
|
||||
public class SpringCoreBlockHoundIntegrationTests {
|
||||
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() {
|
||||
static void setup() {
|
||||
BlockHound.builder()
|
||||
.with(new ReactorBlockHoundIntegration()) // Reactor non-blocking thread predicate
|
||||
.with(new ReactorBlockHoundIntegration()) // Reactor non-blocking thread predicate
|
||||
.with(new ReactiveAdapterRegistry.SpringCoreBlockHoundIntegration())
|
||||
.install();
|
||||
}
|
||||
|
||||
+47
@@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
@@ -77,6 +78,7 @@ import static org.springframework.core.annotation.AnnotationUtilsTests.asArray;
|
||||
* @see AnnotationUtilsTests
|
||||
* @see MultipleComposedAnnotationsOnSingleAnnotatedElementTests
|
||||
* @see ComposedRepeatableAnnotationsTests
|
||||
* @see NestedRepeatableAnnotationsTests
|
||||
*/
|
||||
class AnnotatedElementUtilsTests {
|
||||
|
||||
@@ -908,6 +910,31 @@ class AnnotatedElementUtilsTests {
|
||||
assertThat(annotation.value()).containsExactly("FromValueAttributeMeta");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.3.25
|
||||
*/
|
||||
@Test // gh-29685
|
||||
void getMergedRepeatableAnnotationsWithContainerWithMultipleAttributes() {
|
||||
Set<StandardRepeatableWithContainerWithMultipleAttributes> repeatableAnnotations =
|
||||
AnnotatedElementUtils.getMergedRepeatableAnnotations(
|
||||
StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class,
|
||||
StandardRepeatableWithContainerWithMultipleAttributes.class);
|
||||
assertThat(repeatableAnnotations).map(StandardRepeatableWithContainerWithMultipleAttributes::value)
|
||||
.containsExactly("a", "b");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.3.25
|
||||
*/
|
||||
@Test // gh-29685
|
||||
void findMergedRepeatableAnnotationsWithContainerWithMultipleAttributes() {
|
||||
Set<StandardRepeatableWithContainerWithMultipleAttributes> repeatableAnnotations =
|
||||
AnnotatedElementUtils.findMergedRepeatableAnnotations(
|
||||
StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class,
|
||||
StandardRepeatableWithContainerWithMultipleAttributes.class);
|
||||
assertThat(repeatableAnnotations).map(StandardRepeatableWithContainerWithMultipleAttributes::value)
|
||||
.containsExactly("a", "b");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -1557,4 +1584,24 @@ class AnnotatedElementUtilsTests {
|
||||
static class ValueAttributeMetaMetaClass {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface StandardContainerWithMultipleAttributes {
|
||||
|
||||
StandardRepeatableWithContainerWithMultipleAttributes[] value();
|
||||
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(StandardContainerWithMultipleAttributes.class)
|
||||
@interface StandardRepeatableWithContainerWithMultipleAttributes {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@StandardRepeatableWithContainerWithMultipleAttributes("a")
|
||||
@StandardRepeatableWithContainerWithMultipleAttributes("b")
|
||||
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -58,24 +58,6 @@ class AttributeMethodsTests {
|
||||
assertThat(getAll(methods)).flatExtracting(Method::getName).containsExactly("intValue", "value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasOnlyValueAttributeWhenHasOnlyValueAttributeReturnsTrue() {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(ValueOnly.class);
|
||||
assertThat(methods.hasOnlyValueAttribute()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasOnlyValueAttributeWhenHasOnlySingleNonValueAttributeReturnsFalse() {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(NonValueOnly.class);
|
||||
assertThat(methods.hasOnlyValueAttribute()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasOnlyValueAttributeWhenHasOnlyMultipleAttributesIncludingValueReturnsFalse() {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||
assertThat(methods.hasOnlyValueAttribute()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexOfNameReturnsIndex() {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||
|
||||
+157
-146
@@ -20,7 +20,9 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -31,193 +33,175 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* Tests for {@link RepeatableContainers}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class RepeatableContainersTests {
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenNonRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.standardRepeatables(), WithNonRepeatable.class,
|
||||
NonRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
@Nested
|
||||
class StandardRepeatableContainersTests {
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenNonRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
NonRepeatableTestCase.class, NonRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenSingleReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
SingleStandardRepeatableTestCase.class, StandardRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenContainerButNotRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
ExplicitRepeatablesTestCase.class, ExplicitContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenContainerReturnsRepeats() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
StandardRepeatablesTestCase.class, StandardContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWithContainerWithMultipleAttributes() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class,
|
||||
StandardContainerWithMultipleAttributes.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenSingleReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.standardRepeatables(),
|
||||
WithSingleStandardRepeatable.class, StandardRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
@Nested
|
||||
class ExplicitRepeatableContainerTests {
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenContainerReturnsRepeats() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.standardRepeatables(), WithStandardRepeatables.class,
|
||||
StandardContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenNonRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class),
|
||||
NonRepeatableTestCase.class, NonRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenContainerButNotRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.standardRepeatables(), WithExplicitRepeatables.class,
|
||||
ExplicitContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenStandardRepeatableContainerReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class),
|
||||
StandardRepeatablesTestCase.class, StandardContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenNonRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class),
|
||||
WithNonRepeatable.class, NonRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenContainerReturnsRepeats() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class),
|
||||
ExplicitRepeatablesTestCase.class, ExplicitContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenStandardRepeatableContainerReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class),
|
||||
WithStandardRepeatables.class, StandardContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenContainerIsNullDeducesContainer() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.of(StandardRepeatable.class, null),
|
||||
StandardRepeatablesTestCase.class, StandardContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenContainerReturnsRepeats() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class),
|
||||
WithExplicitRepeatables.class, ExplicitContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenHasNoValueThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class)
|
||||
.isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidNoValue.class))
|
||||
.withMessageContaining("Invalid declaration of container type [%s] for repeatable annotation [%s]",
|
||||
InvalidNoValue.class.getName(), ExplicitRepeatable.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenHasNoValueThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, InvalidNoValue.class))
|
||||
.withMessageContaining("Invalid declaration of container type ["
|
||||
+ InvalidNoValue.class.getName()
|
||||
+ "] for repeatable annotation ["
|
||||
+ ExplicitRepeatable.class.getName() + "]");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenValueIsNotArrayThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class)
|
||||
.isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidNotArray.class))
|
||||
.withMessage("Container type [%s] must declare a 'value' attribute for an array of type [%s]",
|
||||
InvalidNotArray.class.getName(), ExplicitRepeatable.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenValueIsNotArrayThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, InvalidNotArray.class))
|
||||
.withMessage("Container type ["
|
||||
+ InvalidNotArray.class.getName()
|
||||
+ "] must declare a 'value' attribute for an array of type ["
|
||||
+ ExplicitRepeatable.class.getName() + "]");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenValueIsArrayOfWrongTypeThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class)
|
||||
.isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidWrongArrayType.class))
|
||||
.withMessage("Container type [%s] must declare a 'value' attribute for an array of type [%s]",
|
||||
InvalidWrongArrayType.class.getName(), ExplicitRepeatable.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenValueIsArrayOfWrongTypeThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, InvalidWrongArrayType.class))
|
||||
.withMessage("Container type ["
|
||||
+ InvalidWrongArrayType.class.getName()
|
||||
+ "] must declare a 'value' attribute for an array of type ["
|
||||
+ ExplicitRepeatable.class.getName() + "]");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenAnnotationIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RepeatableContainers.of(null, null))
|
||||
.withMessage("Repeatable must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenAnnotationIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
RepeatableContainers.of(null, null))
|
||||
.withMessage("Repeatable must not be null");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenContainerIsNullAndNotRepeatableThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, null))
|
||||
.withMessage("Annotation type must be a repeatable annotation: failed to resolve container type for %s",
|
||||
ExplicitRepeatable.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenContainerIsNullDeducesContainer() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(StandardRepeatable.class, null),
|
||||
WithStandardRepeatables.class, StandardContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenContainerIsNullAndNotRepeatableThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, null))
|
||||
.withMessage("Annotation type must be a repeatable annotation: " +
|
||||
"failed to resolve container type for " +
|
||||
ExplicitRepeatable.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardAndExplicitReturnsRepeats() {
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables().and(
|
||||
ExplicitContainer.class, ExplicitRepeatable.class);
|
||||
assertThat(findRepeatedAnnotationValues(repeatableContainers,
|
||||
WithStandardRepeatables.class, StandardContainer.class)).containsExactly(
|
||||
"a", "b");
|
||||
assertThat(findRepeatedAnnotationValues(repeatableContainers,
|
||||
WithExplicitRepeatables.class, ExplicitContainer.class)).containsExactly(
|
||||
"a", "b");
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables()
|
||||
.and(ExplicitContainer.class, ExplicitRepeatable.class);
|
||||
assertThat(findRepeatedAnnotationValues(repeatableContainers, StandardRepeatablesTestCase.class, StandardContainer.class))
|
||||
.containsExactly("a", "b");
|
||||
assertThat(findRepeatedAnnotationValues(repeatableContainers, ExplicitRepeatablesTestCase.class, ExplicitContainer.class))
|
||||
.containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void noneAlwaysReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.none(), WithStandardRepeatables.class,
|
||||
StandardContainer.class);
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.none(), StandardRepeatablesTestCase.class,
|
||||
StandardContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashcode() {
|
||||
RepeatableContainers c1 = RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class);
|
||||
RepeatableContainers c2 = RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class);
|
||||
RepeatableContainers c1 = RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class);
|
||||
RepeatableContainers c2 = RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class);
|
||||
RepeatableContainers c3 = RepeatableContainers.standardRepeatables();
|
||||
RepeatableContainers c4 = RepeatableContainers.standardRepeatables().and(
|
||||
ExplicitContainer.class, ExplicitRepeatable.class);
|
||||
assertThat(c1.hashCode()).isEqualTo(c2.hashCode());
|
||||
RepeatableContainers c4 = RepeatableContainers.standardRepeatables().and(ExplicitContainer.class, ExplicitRepeatable.class);
|
||||
assertThat(c1).hasSameHashCodeAs(c2);
|
||||
assertThat(c1).isEqualTo(c1).isEqualTo(c2);
|
||||
assertThat(c1).isNotEqualTo(c3).isNotEqualTo(c4);
|
||||
}
|
||||
|
||||
private Object[] findRepeatedAnnotationValues(RepeatableContainers containers,
|
||||
|
||||
private static Object[] findRepeatedAnnotationValues(RepeatableContainers containers,
|
||||
Class<?> element, Class<? extends Annotation> annotationType) {
|
||||
Annotation[] annotations = containers.findRepeatedAnnotations(
|
||||
element.getAnnotation(annotationType));
|
||||
Annotation[] annotations = containers.findRepeatedAnnotations(element.getAnnotation(annotationType));
|
||||
return extractValues(annotations);
|
||||
}
|
||||
|
||||
private Object[] extractValues(Annotation[] annotations) {
|
||||
try {
|
||||
if (annotations == null) {
|
||||
return null;
|
||||
}
|
||||
Object[] result = new String[annotations.length];
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
result[i] = annotations[i].annotationType().getMethod("value").invoke(
|
||||
annotations[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
private static Object[] extractValues(Annotation[] annotations) {
|
||||
if (annotations == null) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.stream(annotations).map(AnnotationUtils::getValue).toArray(Object[]::new);
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface NonRepeatable {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(StandardContainer.class)
|
||||
@interface StandardRepeatable {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface StandardContainer {
|
||||
|
||||
@@ -225,7 +209,8 @@ class RepeatableContainersTests {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ExplicitRepeatable {
|
||||
@Repeatable(StandardContainer.class)
|
||||
@interface StandardRepeatable {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
@@ -236,6 +221,12 @@ class RepeatableContainersTests {
|
||||
ExplicitRepeatable[] value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ExplicitRepeatable {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface InvalidNoValue {
|
||||
}
|
||||
@@ -253,20 +244,40 @@ class RepeatableContainersTests {
|
||||
}
|
||||
|
||||
@NonRepeatable("a")
|
||||
static class WithNonRepeatable {
|
||||
static class NonRepeatableTestCase {
|
||||
}
|
||||
|
||||
@StandardRepeatable("a")
|
||||
static class WithSingleStandardRepeatable {
|
||||
static class SingleStandardRepeatableTestCase {
|
||||
}
|
||||
|
||||
@StandardRepeatable("a")
|
||||
@StandardRepeatable("b")
|
||||
static class WithStandardRepeatables {
|
||||
static class StandardRepeatablesTestCase {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface StandardContainerWithMultipleAttributes {
|
||||
|
||||
StandardRepeatableWithContainerWithMultipleAttributes[] value();
|
||||
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(StandardContainerWithMultipleAttributes.class)
|
||||
@interface StandardRepeatableWithContainerWithMultipleAttributes {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@StandardRepeatableWithContainerWithMultipleAttributes("a")
|
||||
@StandardRepeatableWithContainerWithMultipleAttributes("b")
|
||||
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
|
||||
}
|
||||
|
||||
@ExplicitContainer({ @ExplicitRepeatable("a"), @ExplicitRepeatable("b") })
|
||||
static class WithExplicitRepeatables {
|
||||
static class ExplicitRepeatablesTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -251,32 +251,28 @@ class ObjectUtilsTests {
|
||||
@Deprecated
|
||||
void hashCodeWithDouble() {
|
||||
double dbl = 9830.43;
|
||||
int expected = (new Double(dbl)).hashCode();
|
||||
assertThat(ObjectUtils.hashCode(dbl)).isEqualTo(expected);
|
||||
assertThat(ObjectUtils.hashCode(dbl)).isEqualTo(Double.hashCode(dbl));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
void hashCodeWithFloat() {
|
||||
float flt = 34.8f;
|
||||
int expected = (Float.valueOf(flt)).hashCode();
|
||||
assertThat(ObjectUtils.hashCode(flt)).isEqualTo(expected);
|
||||
assertThat(ObjectUtils.hashCode(flt)).isEqualTo(Float.hashCode(flt));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
void hashCodeWithLong() {
|
||||
long lng = 883L;
|
||||
int expected = (Long.valueOf(lng)).hashCode();
|
||||
assertThat(ObjectUtils.hashCode(lng)).isEqualTo(expected);
|
||||
assertThat(ObjectUtils.hashCode(lng)).isEqualTo(Long.hashCode(lng));
|
||||
}
|
||||
|
||||
@Test
|
||||
void identityToString() {
|
||||
Object obj = new Object();
|
||||
String expected = obj.getClass().getName() + "@" + ObjectUtils.getIdentityHexString(obj);
|
||||
String actual = ObjectUtils.identityToString(obj);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
assertThat(ObjectUtils.identityToString(obj)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -732,7 +728,7 @@ class ObjectUtilsTests {
|
||||
|
||||
@Test
|
||||
void nullSafeToStringWithObjectArray() {
|
||||
Object[] array = {"Han", Long.valueOf(43)};
|
||||
Object[] array = {"Han", 43};
|
||||
assertThat(ObjectUtils.nullSafeToString(array)).isEqualTo("{Han, 43}");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,8 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link StreamUtils}.
|
||||
@@ -57,53 +55,47 @@ class StreamUtilsTests {
|
||||
|
||||
@Test
|
||||
void copyToByteArray() throws Exception {
|
||||
InputStream inputStream = spy(new ByteArrayInputStream(bytes));
|
||||
InputStream inputStream = new ByteArrayInputStream(bytes);
|
||||
byte[] actual = StreamUtils.copyToByteArray(inputStream);
|
||||
assertThat(actual).isEqualTo(bytes);
|
||||
verify(inputStream, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyToString() throws Exception {
|
||||
Charset charset = Charset.defaultCharset();
|
||||
InputStream inputStream = spy(new ByteArrayInputStream(string.getBytes(charset)));
|
||||
InputStream inputStream = new ByteArrayInputStream(string.getBytes(charset));
|
||||
String actual = StreamUtils.copyToString(inputStream, charset);
|
||||
assertThat(actual).isEqualTo(string);
|
||||
verify(inputStream, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyBytes() throws Exception {
|
||||
ByteArrayOutputStream out = spy(new ByteArrayOutputStream());
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
StreamUtils.copy(bytes, out);
|
||||
assertThat(out.toByteArray()).isEqualTo(bytes);
|
||||
verify(out, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyString() throws Exception {
|
||||
Charset charset = Charset.defaultCharset();
|
||||
ByteArrayOutputStream out = spy(new ByteArrayOutputStream());
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
StreamUtils.copy(string, charset, out);
|
||||
assertThat(out.toByteArray()).isEqualTo(string.getBytes(charset));
|
||||
verify(out, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStream() throws Exception {
|
||||
ByteArrayOutputStream out = spy(new ByteArrayOutputStream());
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
StreamUtils.copy(new ByteArrayInputStream(bytes), out);
|
||||
assertThat(out.toByteArray()).isEqualTo(bytes);
|
||||
verify(out, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyRange() throws Exception {
|
||||
ByteArrayOutputStream out = spy(new ByteArrayOutputStream());
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
StreamUtils.copyRange(new ByteArrayInputStream(bytes), out, 0, 100);
|
||||
byte[] range = Arrays.copyOfRange(bytes, 0, 101);
|
||||
assertThat(out.toByteArray()).isEqualTo(range);
|
||||
verify(out, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+10
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -264,7 +264,15 @@ public enum SpelMessage {
|
||||
|
||||
/** @since 5.3.17 */
|
||||
MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075,
|
||||
"Array declares too many elements, exceeding the threshold of ''{0}''");
|
||||
"Array declares too many elements, exceeding the threshold of ''{0}''"),
|
||||
|
||||
/** @since 5.3.26 */
|
||||
MAX_REPEATED_TEXT_SIZE_EXCEEDED(Kind.ERROR, 1076,
|
||||
"Repeated text results in too many characters, exceeding the threshold of ''{0}''"),
|
||||
|
||||
/** @since 5.3.26 */
|
||||
MAX_REGEX_LENGTH_EXCEEDED(Kind.ERROR, 1077,
|
||||
"Regular expression contains too many characters, exceeding the threshold of ''{0}''");
|
||||
|
||||
|
||||
private final Kind kind;
|
||||
|
||||
+37
-13
@@ -22,6 +22,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -46,10 +47,15 @@ import org.springframework.util.Assert;
|
||||
* Represents the invocation of a constructor. Either a constructor on a regular type or
|
||||
* construction of an array. When an array is constructed, an initializer can be specified.
|
||||
*
|
||||
* <p>Examples:<br>
|
||||
* new String('hello world')<br>
|
||||
* new int[]{1,2,3,4}<br>
|
||||
* new int[3] new int[3]{1,2,3}
|
||||
* <h4>Examples</h4>
|
||||
* <ul>
|
||||
* <li><code>new example.Foo()</code></li>
|
||||
* <li><code>new String('hello world')</code></li>
|
||||
* <li><code>new int[] {1,2,3,4}</code></li>
|
||||
* <li><code>new String[] {'abc','xyz'}</code></li>
|
||||
* <li><code>new int[5]</code></li>
|
||||
* <li><code>new int[3][4]</code></li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
@@ -68,7 +74,7 @@ public class ConstructorReference extends SpelNodeImpl {
|
||||
private final boolean isArrayConstructor;
|
||||
|
||||
@Nullable
|
||||
private SpelNodeImpl[] dimensions;
|
||||
private final SpelNodeImpl[] dimensions;
|
||||
|
||||
// TODO is this caching safe - passing the expression around will mean this executor is also being passed around
|
||||
/** The cached executor that may be reused on subsequent evaluations. */
|
||||
@@ -83,6 +89,7 @@ public class ConstructorReference extends SpelNodeImpl {
|
||||
public ConstructorReference(int startPos, int endPos, SpelNodeImpl... arguments) {
|
||||
super(startPos, endPos, arguments);
|
||||
this.isArrayConstructor = false;
|
||||
this.dimensions = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,16 +221,33 @@ public class ConstructorReference extends SpelNodeImpl {
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder("new ");
|
||||
int index = 0;
|
||||
sb.append(getChild(index++).toStringAST());
|
||||
sb.append('(');
|
||||
for (int i = index; i < getChildCount(); i++) {
|
||||
if (i > index) {
|
||||
sb.append(',');
|
||||
sb.append(getChild(0).toStringAST()); // constructor or array type
|
||||
|
||||
// Arrays
|
||||
if (this.isArrayConstructor) {
|
||||
if (hasInitializer()) {
|
||||
// new int[] {1, 2, 3, 4, 5}, etc.
|
||||
InlineList initializer = (InlineList) getChild(1);
|
||||
sb.append("[] ").append(initializer.toStringAST());
|
||||
}
|
||||
else {
|
||||
// new int[3], new java.lang.String[3][4], etc.
|
||||
for (SpelNodeImpl dimension : this.dimensions) {
|
||||
sb.append('[').append(dimension.toStringAST()).append(']');
|
||||
}
|
||||
}
|
||||
sb.append(getChild(i).toStringAST());
|
||||
}
|
||||
sb.append(')');
|
||||
// Constructors
|
||||
else {
|
||||
// new String('hello'), new org.example.Person('Jane', 32), etc.
|
||||
StringJoiner sj = new StringJoiner(",", "(", ")");
|
||||
int count = getChildCount();
|
||||
for (int i = 1; i < count; i++) {
|
||||
sj.add(getChild(i).toStringAST());
|
||||
}
|
||||
sb.append(sj.toString());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -144,7 +144,7 @@ public class FunctionReference extends SpelNodeImpl {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
sj.add(getChild(i).toStringAST());
|
||||
}
|
||||
return '#' + this.name + sj.toString();
|
||||
return '#' + this.name + sj;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -263,7 +263,7 @@ public class MethodReference extends SpelNodeImpl {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
sj.add(getChild(i).toStringAST());
|
||||
}
|
||||
return this.name + sj.toString();
|
||||
return this.name + sj;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,12 +288,9 @@ public class MethodReference extends SpelNodeImpl {
|
||||
if (executor.didArgumentConversionOccur()) {
|
||||
return false;
|
||||
}
|
||||
Class<?> clazz = executor.getMethod().getDeclaringClass();
|
||||
if (!Modifier.isPublic(clazz.getModifiers()) && executor.getPublicDeclaringClass() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
Class<?> clazz = executor.getMethod().getDeclaringClass();
|
||||
return (Modifier.isPublic(clazz.getModifiers()) || executor.getPublicDeclaringClass() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+24
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,6 +25,8 @@ import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
@@ -52,6 +54,13 @@ import org.springframework.util.NumberUtils;
|
||||
*/
|
||||
public class OpMultiply extends Operator {
|
||||
|
||||
/**
|
||||
* Maximum number of characters permitted in repeated text.
|
||||
* @since 5.3.26
|
||||
*/
|
||||
private static final int MAX_REPEATED_TEXT_SIZE = 256;
|
||||
|
||||
|
||||
public OpMultiply(int startPos, int endPos, SpelNodeImpl... operands) {
|
||||
super("*", startPos, endPos, operands);
|
||||
}
|
||||
@@ -109,10 +118,13 @@ public class OpMultiply extends Operator {
|
||||
}
|
||||
|
||||
if (leftOperand instanceof String && rightOperand instanceof Integer) {
|
||||
int repeats = (Integer) rightOperand;
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
result.append(leftOperand);
|
||||
String text = (String) leftOperand;
|
||||
int count = (Integer) rightOperand;
|
||||
int requestedSize = text.length() * count;
|
||||
checkRepeatedTextSize(requestedSize);
|
||||
StringBuilder result = new StringBuilder(requestedSize);
|
||||
for (int i = 0; i < count; i++) {
|
||||
result.append(text);
|
||||
}
|
||||
return new TypedValue(result.toString());
|
||||
}
|
||||
@@ -120,6 +132,13 @@ public class OpMultiply extends Operator {
|
||||
return state.operate(Operation.MULTIPLY, leftOperand, rightOperand);
|
||||
}
|
||||
|
||||
private void checkRepeatedTextSize(int requestedSize) {
|
||||
if (requestedSize > MAX_REPEATED_TEXT_SIZE) {
|
||||
throw new SpelEvaluationException(getStartPosition(),
|
||||
SpelMessage.MAX_REPEATED_TEXT_SIZE_EXCEEDED, MAX_REPEATED_TEXT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompilable() {
|
||||
if (!getLeftOperand().isCompilable()) {
|
||||
|
||||
+43
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,19 +36,41 @@ import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OperatorMatches extends Operator {
|
||||
|
||||
private static final int PATTERN_ACCESS_THRESHOLD = 1000000;
|
||||
|
||||
private final ConcurrentMap<String, Pattern> patternCache = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* Maximum number of characters permitted in a regular expression.
|
||||
* @since 5.3.26
|
||||
*/
|
||||
private static final int MAX_REGEX_LENGTH = 256;
|
||||
|
||||
private final ConcurrentMap<String, Pattern> patternCache;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link OperatorMatches} instance.
|
||||
* @deprecated as of Spring Framework 5.3.26 in favor of invoking
|
||||
* {@link #OperatorMatches(ConcurrentMap, int, int, SpelNodeImpl...)}
|
||||
* with a shared pattern cache instead
|
||||
*/
|
||||
@Deprecated
|
||||
public OperatorMatches(int startPos, int endPos, SpelNodeImpl... operands) {
|
||||
super("matches", startPos, endPos, operands);
|
||||
this(new ConcurrentHashMap<>(), startPos, endPos, operands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link OperatorMatches} instance with a shared pattern cache.
|
||||
* @since 5.3.26
|
||||
*/
|
||||
public OperatorMatches(ConcurrentMap<String, Pattern> patternCache, int startPos, int endPos, SpelNodeImpl... operands) {
|
||||
super("matches", startPos, endPos, operands);
|
||||
this.patternCache = patternCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the first operand matches the regex specified as the second operand.
|
||||
@@ -62,26 +84,28 @@ public class OperatorMatches extends Operator {
|
||||
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
SpelNodeImpl leftOp = getLeftOperand();
|
||||
SpelNodeImpl rightOp = getRightOperand();
|
||||
String left = leftOp.getValue(state, String.class);
|
||||
Object right = getRightOperand().getValue(state);
|
||||
|
||||
if (left == null) {
|
||||
String input = leftOp.getValue(state, String.class);
|
||||
if (input == null) {
|
||||
throw new SpelEvaluationException(leftOp.getStartPosition(),
|
||||
SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, (Object) null);
|
||||
}
|
||||
|
||||
Object right = rightOp.getValue(state);
|
||||
if (!(right instanceof String)) {
|
||||
throw new SpelEvaluationException(rightOp.getStartPosition(),
|
||||
SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right);
|
||||
}
|
||||
String regex = (String) right;
|
||||
|
||||
try {
|
||||
String rightString = (String) right;
|
||||
Pattern pattern = this.patternCache.get(rightString);
|
||||
Pattern pattern = this.patternCache.get(regex);
|
||||
if (pattern == null) {
|
||||
pattern = Pattern.compile(rightString);
|
||||
this.patternCache.putIfAbsent(rightString, pattern);
|
||||
checkRegexLength(regex);
|
||||
pattern = Pattern.compile(regex);
|
||||
this.patternCache.putIfAbsent(regex, pattern);
|
||||
}
|
||||
Matcher matcher = pattern.matcher(new MatcherInput(left, new AccessCount()));
|
||||
Matcher matcher = pattern.matcher(new MatcherInput(input, new AccessCount()));
|
||||
return BooleanTypedValue.forValue(matcher.matches());
|
||||
}
|
||||
catch (PatternSyntaxException ex) {
|
||||
@@ -94,6 +118,13 @@ public class OperatorMatches extends Operator {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRegexLength(String regex) {
|
||||
if (regex.length() > MAX_REGEX_LENGTH) {
|
||||
throw new SpelEvaluationException(getStartPosition(),
|
||||
SpelMessage.MAX_REGEX_LENGTH_EXCEEDED, MAX_REGEX_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class AccessCount {
|
||||
|
||||
@@ -111,7 +142,7 @@ public class OperatorMatches extends Operator {
|
||||
|
||||
private final CharSequence value;
|
||||
|
||||
private AccessCount access;
|
||||
private final AccessCount access;
|
||||
|
||||
public MatcherInput(CharSequence value, AccessCount access) {
|
||||
this.value = value;
|
||||
|
||||
+17
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StringLiteral extends Literal {
|
||||
@@ -36,9 +37,19 @@ public class StringLiteral extends Literal {
|
||||
public StringLiteral(String payload, int startPos, int endPos, String value) {
|
||||
super(payload, startPos, endPos);
|
||||
|
||||
// The original enclosing quote character for the string literal: ' or ".
|
||||
char quoteCharacter = value.charAt(0);
|
||||
|
||||
// Remove enclosing quotes
|
||||
String valueWithinQuotes = value.substring(1, value.length() - 1);
|
||||
valueWithinQuotes = StringUtils.replace(valueWithinQuotes, "''", "'");
|
||||
valueWithinQuotes = StringUtils.replace(valueWithinQuotes, "\"\"", "\"");
|
||||
|
||||
// Replace escaped internal quote characters
|
||||
if (quoteCharacter == '\'') {
|
||||
valueWithinQuotes = StringUtils.replace(valueWithinQuotes, "''", "'");
|
||||
}
|
||||
else {
|
||||
valueWithinQuotes = StringUtils.replace(valueWithinQuotes, "\"\"", "\"");
|
||||
}
|
||||
|
||||
this.value = new TypedValue(valueWithinQuotes);
|
||||
this.exitTypeDescriptor = "Ljava/lang/String";
|
||||
@@ -52,7 +63,9 @@ public class StringLiteral extends Literal {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "'" + getLiteralValue().getValue() + "'";
|
||||
String ast = String.valueOf(getLiteralValue().getValue());
|
||||
ast = StringUtils.replace(ast, "'", "''");
|
||||
return "'" + ast + "'";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+8
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.expression.ParseException;
|
||||
@@ -83,6 +85,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
@@ -95,6 +98,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
// For rules that build nodes, they are stacked here for return
|
||||
private final Deque<SpelNodeImpl> constructedNodes = new ArrayDeque<>();
|
||||
|
||||
// Shared cache for compiled regex patterns
|
||||
private final ConcurrentMap<String, Pattern> patternCache = new ConcurrentHashMap<>();
|
||||
|
||||
// The expression being parsed
|
||||
private String expressionString = "";
|
||||
|
||||
@@ -248,7 +254,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
}
|
||||
|
||||
if (tk == TokenKind.MATCHES) {
|
||||
return new OperatorMatches(t.startPos, t.endPos, expr, rhExpr);
|
||||
return new OperatorMatches(this.patternCache, t.startPos, t.endPos, expr, rhExpr);
|
||||
}
|
||||
|
||||
Assert.isTrue(tk == TokenKind.BETWEEN, "Between token expected");
|
||||
|
||||
+12
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,6 +48,7 @@ import org.springframework.lang.Nullable;
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
* @see StandardEvaluationContext#addMethodResolver(MethodResolver)
|
||||
*/
|
||||
@@ -225,8 +226,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
if (targetObject instanceof Class) {
|
||||
Set<Method> result = new LinkedHashSet<>();
|
||||
// Add these so that static methods are invocable on the type: e.g. Float.valueOf(..)
|
||||
Method[] methods = getMethods(type);
|
||||
for (Method method : methods) {
|
||||
for (Method method : getMethods(type)) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
result.add(method);
|
||||
}
|
||||
@@ -239,19 +239,23 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
Set<Method> result = new LinkedHashSet<>();
|
||||
// Expose interface methods (not proxy-declared overrides) for proper vararg introspection
|
||||
for (Class<?> ifc : type.getInterfaces()) {
|
||||
Method[] methods = getMethods(ifc);
|
||||
for (Method method : methods) {
|
||||
for (Method method : getMethods(ifc)) {
|
||||
if (isCandidateForInvocation(method, type)) {
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure methods defined in java.lang.Object are exposed for JDK proxies.
|
||||
for (Method method : getMethods(Object.class)) {
|
||||
if (isCandidateForInvocation(method, type)) {
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
Set<Method> result = new LinkedHashSet<>();
|
||||
Method[] methods = getMethods(type);
|
||||
for (Method method : methods) {
|
||||
for (Method method : getMethods(type)) {
|
||||
if (isCandidateForInvocation(method, type)) {
|
||||
result.add(method);
|
||||
}
|
||||
@@ -276,7 +280,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
* Determine whether the given {@code Method} is a candidate for method resolution
|
||||
* on an instance of the given target class.
|
||||
* <p>The default implementation considers any method as a candidate, even for
|
||||
* static methods sand non-user-declared methods on the {@link Object} base class.
|
||||
* static methods and non-user-declared methods on the {@link Object} base class.
|
||||
* @param method the Method to evaluate
|
||||
* @param targetClass the concrete target class that is being introspected
|
||||
* @since 4.3.15
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*/
|
||||
public abstract class AbstractExpressionTests {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
protected static final boolean DEBUG = false;
|
||||
|
||||
protected static final boolean SHOULD_BE_WRITABLE = true;
|
||||
|
||||
@@ -202,7 +202,9 @@ public abstract class AbstractExpressionTests {
|
||||
protected void parseAndCheckError(String expression, SpelMessage expectedMessage, Object... otherProperties) {
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
|
||||
Expression expr = parser.parseExpression(expression);
|
||||
SpelUtilities.printAbstractSyntaxTree(System.out, expr);
|
||||
if (DEBUG) {
|
||||
SpelUtilities.printAbstractSyntaxTree(System.out, expr);
|
||||
}
|
||||
}).satisfies(ex -> {
|
||||
assertThat(ex.getMessageCode()).isEqualTo(expectedMessage);
|
||||
if (otherProperties != null && otherProperties.length != 0) {
|
||||
|
||||
+82
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -127,17 +127,6 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
evaluate("null?.null?.null", null, null);
|
||||
}
|
||||
|
||||
@Test // SPR-16731
|
||||
void matchesWithPatternAccessThreshold() {
|
||||
String pattern = "^(?=[a-z0-9-]{1,47})([a-z0-9]+[-]{0,1}){1,47}[a-z0-9]{1}$";
|
||||
String expression = "'abcde-fghijklmn-o42pasdfasdfasdf.qrstuvwxyz10x.xx.yyy.zasdfasfd' matches \'" + pattern + "\'";
|
||||
Expression expr = parser.parseExpression(expression);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(expr::getValue)
|
||||
.withCauseInstanceOf(IllegalStateException.class)
|
||||
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.FLAWED_PATTERN));
|
||||
}
|
||||
|
||||
// mixing operators
|
||||
@Test
|
||||
void mixingOperators() {
|
||||
@@ -364,6 +353,53 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class StringLiterals {
|
||||
|
||||
@Test
|
||||
void insideSingleQuotes() {
|
||||
evaluate("'hello'", "hello", String.class);
|
||||
evaluate("'hello world'", "hello world", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void insideDoubleQuotes() {
|
||||
evaluate("\"hello\"", "hello", String.class);
|
||||
evaluate("\"hello world\"", "hello world", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleQuotesInsideSingleQuotes() {
|
||||
evaluate("'Tony''s Pizza'", "Tony's Pizza", String.class);
|
||||
evaluate("'big ''''pizza'''' parlor'", "big ''pizza'' parlor", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doubleQuotesInsideDoubleQuotes() {
|
||||
evaluate("\"big \"\"pizza\"\" parlor\"", "big \"pizza\" parlor", String.class);
|
||||
evaluate("\"big \"\"\"\"pizza\"\"\"\" parlor\"", "big \"\"pizza\"\" parlor", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleQuotesInsideDoubleQuotes() {
|
||||
evaluate("\"Tony's Pizza\"", "Tony's Pizza", String.class);
|
||||
evaluate("\"big ''pizza'' parlor\"", "big ''pizza'' parlor", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doubleQuotesInsideSingleQuotes() {
|
||||
evaluate("'big \"pizza\" parlor'", "big \"pizza\" parlor", String.class);
|
||||
evaluate("'two double \"\" quotes'", "two double \"\" quotes", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void inCompoundExpressions() {
|
||||
evaluate("'123''4' == '123''4'", true, Boolean.class);
|
||||
evaluate("\"123\"\"4\" == \"123\"\"4\"", true, Boolean.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class RelationalOperatorTests {
|
||||
|
||||
@@ -413,28 +449,49 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsMatches01() {
|
||||
evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "false", Boolean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsMatches02() {
|
||||
void matchesTrue() {
|
||||
evaluate("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "true", Boolean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsMatches03() {
|
||||
void matchesFalse() {
|
||||
evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "false", Boolean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWithInputConversion() {
|
||||
evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int --> string
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWithNullInput() {
|
||||
evaluateAndCheckError("null matches '^.*$'", SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, 0, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsMatches04() {
|
||||
void matchesWithNullPattern() {
|
||||
evaluateAndCheckError("'abc' matches null", SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, 14, null);
|
||||
}
|
||||
|
||||
@Test // SPR-16731
|
||||
void matchesWithPatternAccessThreshold() {
|
||||
String pattern = "^(?=[a-z0-9-]{1,47})([a-z0-9]+[-]{0,1}){1,47}[a-z0-9]{1}$";
|
||||
String expression = "'abcde-fghijklmn-o42pasdfasdfasdf.qrstuvwxyz10x.xx.yyy.zasdfasfd' matches '" + pattern + "'";
|
||||
evaluateAndCheckError(expression, SpelMessage.FLAWED_PATTERN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsMatches05() {
|
||||
evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int>string
|
||||
void matchesWithPatternLengthThreshold() {
|
||||
String pattern = "(0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
||||
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
||||
"01234567890123456789012345678901234567890123456789|abc)";
|
||||
assertThat(pattern).hasSize(256);
|
||||
Expression expr = parser.parseExpression("'abc' matches '" + pattern + "'");
|
||||
assertThat(expr.getValue(context, Boolean.class)).isTrue();
|
||||
|
||||
pattern += "?";
|
||||
assertThat(pattern).hasSize(257);
|
||||
evaluateAndCheckError("'abc' matches '" + pattern + "'", Boolean.class, SpelMessage.MAX_REGEX_LENGTH_EXCEEDED);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1401,7 +1458,9 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
private void expectFail(ExpressionParser parser, EvaluationContext eContext, String expressionString, SpelMessage messageCode) {
|
||||
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> {
|
||||
Expression e = parser.parseExpression(expressionString);
|
||||
SpelUtilities.printAbstractSyntaxTree(System.out, e);
|
||||
if (DEBUG) {
|
||||
SpelUtilities.printAbstractSyntaxTree(System.out, e);
|
||||
}
|
||||
e.getValue(eContext);
|
||||
}).satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(messageCode));
|
||||
}
|
||||
|
||||
+41
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,52 +29,70 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
/**
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class LiteralExpressionTests {
|
||||
class LiteralExpressionTests {
|
||||
|
||||
private final LiteralExpression lEx = new LiteralExpression("somevalue");
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetValue() throws Exception {
|
||||
LiteralExpression lEx = new LiteralExpression("somevalue");
|
||||
assertThat(lEx.getValue()).isInstanceOf(String.class).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(String.class)).isInstanceOf(String.class).isEqualTo("somevalue");
|
||||
void getValue() throws Exception {
|
||||
assertThat(lEx.getValue()).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(String.class)).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(new Rooty())).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(new Rooty(), String.class)).isEqualTo("somevalue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getValueWithSuppliedEvaluationContext() throws Exception {
|
||||
EvaluationContext ctx = new StandardEvaluationContext();
|
||||
assertThat(lEx.getValue(ctx)).isInstanceOf(String.class).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(ctx, String.class)).isInstanceOf(String.class).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(new Rooty())).isInstanceOf(String.class).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(new Rooty(), String.class)).isInstanceOf(String.class).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(ctx, new Rooty())).isInstanceOf(String.class).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(ctx, new Rooty(),String.class)).isInstanceOf(String.class).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(ctx)).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(ctx, String.class)).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(ctx, new Rooty())).isEqualTo("somevalue");
|
||||
assertThat(lEx.getValue(ctx, new Rooty(), String.class)).isEqualTo("somevalue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getExpressionString() {
|
||||
assertThat(lEx.getExpressionString()).isEqualTo("somevalue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isWritable() throws Exception {
|
||||
assertThat(lEx.isWritable(new StandardEvaluationContext())).isFalse();
|
||||
assertThat(lEx.isWritable(new Rooty())).isFalse();
|
||||
assertThat(lEx.isWritable(new StandardEvaluationContext(), new Rooty())).isFalse();
|
||||
}
|
||||
|
||||
static class Rooty {}
|
||||
|
||||
@Test
|
||||
public void testSetValue() {
|
||||
assertThatExceptionOfType(EvaluationException.class).isThrownBy(() ->
|
||||
new LiteralExpression("somevalue").setValue(new StandardEvaluationContext(), "flibble"))
|
||||
void setValue() {
|
||||
assertThatExceptionOfType(EvaluationException.class)
|
||||
.isThrownBy(() -> lEx.setValue(new StandardEvaluationContext(), "flibble"))
|
||||
.satisfies(ex -> assertThat(ex.getExpressionString()).isEqualTo("somevalue"));
|
||||
assertThatExceptionOfType(EvaluationException.class).isThrownBy(() ->
|
||||
new LiteralExpression("somevalue").setValue(new Rooty(), "flibble"))
|
||||
assertThatExceptionOfType(EvaluationException.class)
|
||||
.isThrownBy(() -> lEx.setValue(new Rooty(), "flibble"))
|
||||
.satisfies(ex -> assertThat(ex.getExpressionString()).isEqualTo("somevalue"));
|
||||
assertThatExceptionOfType(EvaluationException.class).isThrownBy(() ->
|
||||
new LiteralExpression("somevalue").setValue(new StandardEvaluationContext(), new Rooty(), "flibble"))
|
||||
assertThatExceptionOfType(EvaluationException.class)
|
||||
.isThrownBy(() -> lEx.setValue(new StandardEvaluationContext(), new Rooty(), "flibble"))
|
||||
.satisfies(ex -> assertThat(ex.getExpressionString()).isEqualTo("somevalue"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetValueType() throws Exception {
|
||||
LiteralExpression lEx = new LiteralExpression("somevalue");
|
||||
void getValueType() throws Exception {
|
||||
assertThat(lEx.getValueType()).isEqualTo(String.class);
|
||||
assertThat(lEx.getValueType(new StandardEvaluationContext())).isEqualTo(String.class);
|
||||
assertThat(lEx.getValueType(new Rooty())).isEqualTo(String.class);
|
||||
assertThat(lEx.getValueType(new StandardEvaluationContext(), new Rooty())).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getValueTypeDescriptor() throws Exception {
|
||||
assertThat(lEx.getValueTypeDescriptor().getType()).isEqualTo(String.class);
|
||||
assertThat(lEx.getValueTypeDescriptor(new StandardEvaluationContext()).getType()).isEqualTo(String.class);
|
||||
assertThat(lEx.getValueTypeDescriptor(new Rooty()).getType()).isEqualTo(String.class);
|
||||
assertThat(lEx.getValueTypeDescriptor(new StandardEvaluationContext(), new Rooty()).getType()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
|
||||
static class Rooty {}
|
||||
|
||||
}
|
||||
|
||||
+65
-54
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,22 +21,25 @@ import java.math.BigInteger;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.ast.Operator;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.expression.spel.SpelMessage.MAX_REPEATED_TEXT_SIZE_EXCEEDED;
|
||||
|
||||
/**
|
||||
* Tests the evaluation of expressions using relational operators.
|
||||
* Tests the evaluation of expressions using various operators.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Giovanni Dall'Oglio Risso
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class OperatorTests extends AbstractExpressionTests {
|
||||
|
||||
@Test
|
||||
void testEqual() {
|
||||
void equal() {
|
||||
evaluate("3 == 5", false, Boolean.class);
|
||||
evaluate("5 == 3", false, Boolean.class);
|
||||
evaluate("6 == 6", true, Boolean.class);
|
||||
@@ -85,7 +88,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotEqual() {
|
||||
void notEqual() {
|
||||
evaluate("3 != 5", true, Boolean.class);
|
||||
evaluate("5 != 3", true, Boolean.class);
|
||||
evaluate("6 != 6", false, Boolean.class);
|
||||
@@ -134,7 +137,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLessThan() {
|
||||
void lessThan() {
|
||||
evaluate("5 < 5", false, Boolean.class);
|
||||
evaluate("3 < 5", true, Boolean.class);
|
||||
evaluate("5 < 3", false, Boolean.class);
|
||||
@@ -176,7 +179,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLessThanOrEqual() {
|
||||
void lessThanOrEqual() {
|
||||
evaluate("3 <= 5", true, Boolean.class);
|
||||
evaluate("5 <= 3", false, Boolean.class);
|
||||
evaluate("6 <= 6", true, Boolean.class);
|
||||
@@ -225,7 +228,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGreaterThan() {
|
||||
void greaterThan() {
|
||||
evaluate("3 > 5", false, Boolean.class);
|
||||
evaluate("5 > 3", true, Boolean.class);
|
||||
evaluate("3L > 5L", false, Boolean.class);
|
||||
@@ -266,7 +269,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGreaterThanOrEqual() {
|
||||
void greaterThanOrEqual() {
|
||||
evaluate("3 >= 5", false, Boolean.class);
|
||||
evaluate("5 >= 3", true, Boolean.class);
|
||||
evaluate("6 >= 6", true, Boolean.class);
|
||||
@@ -315,27 +318,22 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIntegerLiteral() {
|
||||
void integerLiteral() {
|
||||
evaluate("3", 3, Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRealLiteral() {
|
||||
void realLiteral() {
|
||||
evaluate("3.5", 3.5d, Double.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiplyStringInt() {
|
||||
evaluate("'a' * 5", "aaaaa", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiplyDoubleDoubleGivesDouble() {
|
||||
void multiplyDoubleDoubleGivesDouble() {
|
||||
evaluate("3.0d * 5.0d", 15.0d, Double.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMixedOperandsBigDecimal() {
|
||||
void mixedOperandsBigDecimal() {
|
||||
evaluate("3 * new java.math.BigDecimal('5')", new BigDecimal("15"), BigDecimal.class);
|
||||
evaluate("3L * new java.math.BigDecimal('5')", new BigDecimal("15"), BigDecimal.class);
|
||||
evaluate("3.0d * new java.math.BigDecimal('5')", new BigDecimal("15.0"), BigDecimal.class);
|
||||
@@ -361,19 +359,19 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMathOperatorAdd02() {
|
||||
void mathOperatorAdd02() {
|
||||
evaluate("'hello' + ' ' + 'world'", "hello world", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMathOperatorsInChains() {
|
||||
void mathOperatorsInChains() {
|
||||
evaluate("1+2+3",6,Integer.class);
|
||||
evaluate("2*3*4",24,Integer.class);
|
||||
evaluate("12-1-2",9,Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIntegerArithmetic() {
|
||||
void integerArithmetic() {
|
||||
evaluate("2 + 4", "6", Integer.class);
|
||||
evaluate("5 - 4", "1", Integer.class);
|
||||
evaluate("3 * 5", 15, Integer.class);
|
||||
@@ -388,7 +386,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPlus() {
|
||||
void plus() {
|
||||
evaluate("7 + 2", "9", Integer.class);
|
||||
evaluate("3.0f + 5.0f", 8.0f, Float.class);
|
||||
evaluate("3.0d + 5.0d", 8.0d, Double.class);
|
||||
@@ -400,44 +398,44 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
evaluate("null + 'ab'", "nullab", String.class);
|
||||
|
||||
// AST:
|
||||
SpelExpression expr = (SpelExpression)parser.parseExpression("+3");
|
||||
SpelExpression expr = (SpelExpression) parser.parseExpression("+3");
|
||||
assertThat(expr.toStringAST()).isEqualTo("+3");
|
||||
expr = (SpelExpression)parser.parseExpression("2+3");
|
||||
expr = (SpelExpression) parser.parseExpression("2+3");
|
||||
assertThat(expr.toStringAST()).isEqualTo("(2 + 3)");
|
||||
|
||||
// use as a unary operator
|
||||
evaluate("+5d",5d,Double.class);
|
||||
evaluate("+5L",5L,Long.class);
|
||||
evaluate("+5",5,Integer.class);
|
||||
evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"),BigDecimal.class);
|
||||
evaluateAndCheckError("+'abc'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
evaluate("+5d", 5d, Double.class);
|
||||
evaluate("+5L", 5L, Long.class);
|
||||
evaluate("+5", 5, Integer.class);
|
||||
evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"), BigDecimal.class);
|
||||
evaluateAndCheckError("+'abc'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
|
||||
// string concatenation
|
||||
evaluate("'abc'+'def'","abcdef",String.class);
|
||||
evaluate("'abc'+'def'", "abcdef", String.class);
|
||||
|
||||
evaluate("5 + new Integer('37')",42,Integer.class);
|
||||
evaluate("5 + new Integer('37')", 42, Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMinus() {
|
||||
void minus() {
|
||||
evaluate("'c' - 2", "a", String.class);
|
||||
evaluate("3.0f - 5.0f", -2.0f, Float.class);
|
||||
evaluateAndCheckError("'ab' - 2", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
evaluateAndCheckError("2-'ab'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
SpelExpression expr = (SpelExpression)parser.parseExpression("-3");
|
||||
SpelExpression expr = (SpelExpression) parser.parseExpression("-3");
|
||||
assertThat(expr.toStringAST()).isEqualTo("-3");
|
||||
expr = (SpelExpression)parser.parseExpression("2-3");
|
||||
expr = (SpelExpression) parser.parseExpression("2-3");
|
||||
assertThat(expr.toStringAST()).isEqualTo("(2 - 3)");
|
||||
|
||||
evaluate("-5d",-5d,Double.class);
|
||||
evaluate("-5L",-5L,Long.class);
|
||||
evaluate("-5d", -5d, Double.class);
|
||||
evaluate("-5L", -5L, Long.class);
|
||||
evaluate("-5", -5, Integer.class);
|
||||
evaluate("-new java.math.BigDecimal('5')", new BigDecimal("-5"),BigDecimal.class);
|
||||
evaluate("-new java.math.BigDecimal('5')", new BigDecimal("-5"), BigDecimal.class);
|
||||
evaluateAndCheckError("-'abc'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModulus() {
|
||||
void modulus() {
|
||||
evaluate("3%2",1,Integer.class);
|
||||
evaluate("3L%2L",1L,Long.class);
|
||||
evaluate("3.0f%2.0f",1f,Float.class);
|
||||
@@ -448,7 +446,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDivide() {
|
||||
void divide() {
|
||||
evaluate("3.0f / 5.0f", 0.6f, Float.class);
|
||||
evaluate("4L/2L",2L,Long.class);
|
||||
evaluate("3.0f div 5.0f", 0.6f, Float.class);
|
||||
@@ -461,17 +459,17 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMathOperatorDivide_ConvertToDouble() {
|
||||
void mathOperatorDivide_ConvertToDouble() {
|
||||
evaluateAndAskForReturnType("8/4", 2.0, Double.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMathOperatorDivide04_ConvertToFloat() {
|
||||
void mathOperatorDivide04_ConvertToFloat() {
|
||||
evaluateAndAskForReturnType("8/4", 2.0F, Float.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoubles() {
|
||||
void doubles() {
|
||||
evaluate("3.0d == 5.0d", false, Boolean.class);
|
||||
evaluate("3.0d == 3.0d", true, Boolean.class);
|
||||
evaluate("3.0d != 5.0d", true, Boolean.class);
|
||||
@@ -484,7 +482,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBigDecimals() {
|
||||
void bigDecimals() {
|
||||
evaluate("3 + new java.math.BigDecimal('5')", new BigDecimal("8"), BigDecimal.class);
|
||||
evaluate("3 - new java.math.BigDecimal('5')", new BigDecimal("-2"), BigDecimal.class);
|
||||
evaluate("3 * new java.math.BigDecimal('5')", new BigDecimal("15"), BigDecimal.class);
|
||||
@@ -495,7 +493,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOperatorNames() {
|
||||
void operatorNames() {
|
||||
Operator node = getOperatorNode((SpelExpression)parser.parseExpression("1==3"));
|
||||
assertThat(node.getOperatorName()).isEqualTo("==");
|
||||
|
||||
@@ -534,22 +532,22 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOperatorOverloading() {
|
||||
void operatorOverloading() {
|
||||
evaluateAndCheckError("'a' * '2'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
evaluateAndCheckError("'a' ^ '2'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPower() {
|
||||
evaluate("3^2",9,Integer.class);
|
||||
evaluate("3.0d^2.0d",9.0d,Double.class);
|
||||
evaluate("3L^2L",9L,Long.class);
|
||||
void power() {
|
||||
evaluate("3^2", 9, Integer.class);
|
||||
evaluate("3.0d^2.0d", 9.0d, Double.class);
|
||||
evaluate("3L^2L", 9L, Long.class);
|
||||
evaluate("(2^32)^2", 9223372036854775807L, Long.class);
|
||||
evaluate("new java.math.BigDecimal('5') ^ 3", new BigDecimal("125"), BigDecimal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMixedOperands_FloatsAndDoubles() {
|
||||
void mixedOperands_FloatsAndDoubles() {
|
||||
evaluate("3.0d + 5.0f", 8.0d, Double.class);
|
||||
evaluate("3.0D - 5.0f", -2.0d, Double.class);
|
||||
evaluate("3.0f * 5.0d", 15.0d, Double.class);
|
||||
@@ -558,7 +556,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMixedOperands_DoublesAndInts() {
|
||||
void mixedOperands_DoublesAndInts() {
|
||||
evaluate("3.0d + 5", 8.0d, Double.class);
|
||||
evaluate("3.0D - 5", -2.0d, Double.class);
|
||||
evaluate("3.0f * 5", 15.0f, Float.class);
|
||||
@@ -569,7 +567,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStrings() {
|
||||
void strings() {
|
||||
evaluate("'abc' == 'abc'", true, Boolean.class);
|
||||
evaluate("'abc' == 'def'", false, Boolean.class);
|
||||
evaluate("'abc' != 'abc'", false, Boolean.class);
|
||||
@@ -577,7 +575,20 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLongs() {
|
||||
void stringRepeat() {
|
||||
evaluate("'abc' * 0", "", String.class);
|
||||
evaluate("'abc' * 1", "abc", String.class);
|
||||
evaluate("'abc' * 2", "abcabc", String.class);
|
||||
|
||||
Expression expr = parser.parseExpression("'a' * 256");
|
||||
assertThat(expr.getValue(context, String.class)).hasSize(256);
|
||||
|
||||
// 4 is the position of the '*' (repeat operator)
|
||||
evaluateAndCheckError("'a' * 257", String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void longs() {
|
||||
evaluate("3L == 4L", false, Boolean.class);
|
||||
evaluate("3L == 3L", true, Boolean.class);
|
||||
evaluate("3L != 4L", true, Boolean.class);
|
||||
@@ -588,7 +599,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBigIntegers() {
|
||||
void bigIntegers() {
|
||||
evaluate("3 + new java.math.BigInteger('5')", new BigInteger("8"), BigInteger.class);
|
||||
evaluate("3 - new java.math.BigInteger('5')", new BigInteger("-2"), BigInteger.class);
|
||||
evaluate("3 * new java.math.BigInteger('5')", new BigInteger("15"), BigInteger.class);
|
||||
@@ -619,7 +630,7 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
|
||||
public static class BaseComparable implements Comparable<BaseComparable> {
|
||||
static class BaseComparable implements Comparable<BaseComparable> {
|
||||
|
||||
@Override
|
||||
public int compareTo(BaseComparable other) {
|
||||
|
||||
+485
-379
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
@@ -37,413 +39,517 @@ class ParsingTests {
|
||||
private final SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
|
||||
// literals
|
||||
@Test
|
||||
void literalBoolean01() {
|
||||
parseCheck("false");
|
||||
@Nested
|
||||
class Miscellaneous {
|
||||
|
||||
@Test
|
||||
void literalNull() {
|
||||
parseCheck("null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalDate01() {
|
||||
parseCheck("date('1974/08/24')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalDate02() {
|
||||
parseCheck("date('19740824T131030','yyyyMMddTHHmmss')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mixedOperators() {
|
||||
parseCheck("true and 5>3", "(true and (5 > 3))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignmentToVariables() {
|
||||
parseCheck("#var1='value1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionProcessorsCountStringArray() {
|
||||
parseCheck("new String[] {'abc','def','xyz'}.count()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionProcessorsCountIntArray() {
|
||||
parseCheck("new int[] {1,2,3}.count()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionProcessorsMax() {
|
||||
parseCheck("new int[] {1,2,3}.max()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionProcessorsMin() {
|
||||
parseCheck("new int[] {1,2,3}.min()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionProcessorsAverage() {
|
||||
parseCheck("new int[] {1,2,3}.average()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionProcessorsSort() {
|
||||
parseCheck("new int[] {3,2,1}.sort()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionProcessorsNonNull() {
|
||||
parseCheck("{'a','b',null,'d',null}.nonNull()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionProcessorsDistinct() {
|
||||
parseCheck("{'a','b','a','d','e'}.distinct()");
|
||||
}
|
||||
|
||||
@Disabled("Unsupported syntax/feature")
|
||||
@Test
|
||||
void lambdaMax() {
|
||||
parseCheck("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))",
|
||||
"(#max={|x,y| ($x > $y) ? $x : $y };#max(5,25))");
|
||||
}
|
||||
|
||||
@Disabled("Unsupported syntax/feature")
|
||||
@Test
|
||||
void lambdaFactorial() {
|
||||
parseCheck("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))",
|
||||
"(#fact={|n| ($n <= 1) ? 1 : ($n * #fact(($n - 1))) };#fact(5))");
|
||||
}
|
||||
|
||||
@Disabled("Unsupported syntax/feature")
|
||||
@Test
|
||||
void projection() {
|
||||
parseCheck("{1,2,3,4,5,6,7,8,9,10}.!{#isEven()}");
|
||||
}
|
||||
|
||||
@Disabled("Unsupported syntax/feature")
|
||||
@Test
|
||||
void selection() {
|
||||
parseCheck("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}",
|
||||
"{1,2,3,4,5,6,7,8,9,10}.?{(#isEven(#this) == 'y')}");
|
||||
}
|
||||
|
||||
@Disabled("Unsupported syntax/feature")
|
||||
@Test
|
||||
void selectionFirst() {
|
||||
parseCheck("{1,2,3,4,5,6,7,8,9,10}.^{#isEven(#this) == 'y'}",
|
||||
"{1,2,3,4,5,6,7,8,9,10}.^{(#isEven(#this) == 'y')}");
|
||||
}
|
||||
|
||||
@Disabled("Unsupported syntax/feature")
|
||||
@Test
|
||||
void selectionLast() {
|
||||
parseCheck("{1,2,3,4,5,6,7,8,9,10}.${#isEven(#this) == 'y'}",
|
||||
"{1,2,3,4,5,6,7,8,9,10}.${(#isEven(#this) == 'y')}");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalLong01() {
|
||||
parseCheck("37L", "37");
|
||||
@Nested
|
||||
class LiteralBooleans {
|
||||
|
||||
@Test
|
||||
void literalBooleanFalse() {
|
||||
parseCheck("false");
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalBooleanTrue() {
|
||||
parseCheck("true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalBooleanNotTrue() {
|
||||
parseCheck("!true");
|
||||
parseCheck("not true", "!true");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalBoolean02() {
|
||||
parseCheck("true");
|
||||
@Nested
|
||||
class LiteralNumbers {
|
||||
|
||||
@Test
|
||||
void literalLong() {
|
||||
parseCheck("37L", "37");
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalIntegers() {
|
||||
parseCheck("1");
|
||||
parseCheck("1415");
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalReal() {
|
||||
parseCheck("6.0221415E+23", "6.0221415E23");
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalHex() {
|
||||
parseCheck("0x7FFFFFFF", "2147483647");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalBoolean03() {
|
||||
parseCheck("!true");
|
||||
@Nested
|
||||
class LiteralStrings {
|
||||
|
||||
@Test
|
||||
void insideSingleQuotes() {
|
||||
parseCheck("'hello'");
|
||||
parseCheck("'hello world'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void insideDoubleQuotes() {
|
||||
parseCheck("\"hello\"", "'hello'");
|
||||
parseCheck("\"hello world\"", "'hello world'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleQuotesInsideSingleQuotes() {
|
||||
parseCheck("'Tony''s Pizza'");
|
||||
parseCheck("'big ''''pizza'''' parlor'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doubleQuotesInsideDoubleQuotes() {
|
||||
parseCheck("\"big \"\"pizza\"\" parlor\"", "'big \"pizza\" parlor'");
|
||||
parseCheck("\"big \"\"\"\"pizza\"\"\"\" parlor\"", "'big \"\"pizza\"\" parlor'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleQuotesInsideDoubleQuotes() {
|
||||
parseCheck("\"Tony's Pizza\"", "'Tony''s Pizza'");
|
||||
parseCheck("\"big ''pizza'' parlor\"", "'big ''''pizza'''' parlor'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doubleQuotesInsideSingleQuotes() {
|
||||
parseCheck("'big \"pizza\" parlor'");
|
||||
parseCheck("'two double \"\" quotes'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void inCompoundExpressions() {
|
||||
parseCheck("'123''4' == '123''4'", "('123''4' == '123''4')");
|
||||
parseCheck("('123''4'=='123''4')", "('123''4' == '123''4')");
|
||||
parseCheck("\"123\"\"4\" == \"123\"\"4\"", "('123\"4' == '123\"4')");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalInteger01() {
|
||||
parseCheck("1");
|
||||
@Nested
|
||||
class BooleanOperators {
|
||||
|
||||
@Test
|
||||
void booleanOperatorsOr01() {
|
||||
parseCheck("false or false", "(false or false)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanOperatorsOr02() {
|
||||
parseCheck("false or true", "(false or true)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanOperatorsOr03() {
|
||||
parseCheck("true or false", "(true or false)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanOperatorsOr04() {
|
||||
parseCheck("true or false", "(true or false)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanOperatorsMix() {
|
||||
parseCheck("false or true and false", "(false or (true and false))");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalInteger02() {
|
||||
parseCheck("1415");
|
||||
@Nested
|
||||
class RelationalOperators {
|
||||
|
||||
@Test
|
||||
void relOperatorsGT() {
|
||||
parseCheck("3>6", "(3 > 6)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsLT() {
|
||||
parseCheck("3<6", "(3 < 6)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsLE() {
|
||||
parseCheck("3<=6", "(3 <= 6)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsGE01() {
|
||||
parseCheck("3>=6", "(3 >= 6)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsGE02() {
|
||||
parseCheck("3>=3", "(3 >= 3)");
|
||||
}
|
||||
|
||||
@Disabled("Unsupported syntax/feature")
|
||||
@Test
|
||||
void relOperatorsIn() {
|
||||
parseCheck("3 in {1,2,3,4,5}", "(3 in {1,2,3,4,5})");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsBetweenNumbers() {
|
||||
parseCheck("1 between {1, 5}", "(1 between {1,5})");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsBetweenStrings() {
|
||||
parseCheck("'efg' between {'abc', 'xyz'}", "('efg' between {'abc','xyz'})");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsInstanceOfInt() {
|
||||
parseCheck("'xyz' instanceof int", "('xyz' instanceof int)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsInstanceOfList() {
|
||||
parseCheck("{1, 2, 3, 4, 5} instanceof List", "({1,2,3,4,5} instanceof List)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsMatches() {
|
||||
parseCheck("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "('5.0067' matches '^-?\\d+(\\.\\d{2})?$')");
|
||||
parseCheck("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "('5.00' matches '^-?\\d+(\\.\\d{2})?$')");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalString01() {
|
||||
parseCheck("'hello'");
|
||||
@Nested
|
||||
class MathematicalOperators {
|
||||
|
||||
@Test
|
||||
void mathOperatorsAddIntegers() {
|
||||
parseCheck("2+4", "(2 + 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsAddStrings() {
|
||||
parseCheck("'a'+'b'", "('a' + 'b')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsAddMultipleStrings() {
|
||||
parseCheck("'hello'+' '+'world'", "(('hello' + ' ') + 'world')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsSubtract() {
|
||||
parseCheck("5-4", "(5 - 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsMultiply() {
|
||||
parseCheck("7*4", "(7 * 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsDivide() {
|
||||
parseCheck("8/4", "(8 / 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorModulus() {
|
||||
parseCheck("7 % 4", "(7 % 4)");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalString02() {
|
||||
parseCheck("'joe bloggs'");
|
||||
@Nested
|
||||
class References {
|
||||
|
||||
@Test
|
||||
void references() {
|
||||
parseCheck("@foo");
|
||||
parseCheck("@'foo.bar'");
|
||||
parseCheck("@\"foo.bar.goo\"" , "@'foo.bar.goo'");
|
||||
parseCheck("@$$foo");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalString03() {
|
||||
parseCheck("'Tony''s Pizza'", "'Tony's Pizza'");
|
||||
@Nested
|
||||
class Properties {
|
||||
|
||||
@Test
|
||||
void propertiesSingle() {
|
||||
parseCheck("name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertiesDouble() {
|
||||
parseCheck("placeofbirth.CitY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertiesMultiple() {
|
||||
parseCheck("a.b.c.d.e");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalReal01() {
|
||||
parseCheck("6.0221415E+23", "6.0221415E23");
|
||||
@Nested
|
||||
class InlineCollections {
|
||||
|
||||
@Test
|
||||
void inlineListOfIntegers() {
|
||||
parseCheck("{1,2,3,4}");
|
||||
parseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void inlineListOfStrings() {
|
||||
parseCheck("{'abc','xyz'}", "{'abc','xyz'}");
|
||||
parseCheck("{\"abc\", 'xyz'}", "{'abc','xyz'}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void inlineMapStringToObject() {
|
||||
parseCheck("{'key1':'Value 1','today':DateTime.Today}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void inlineMapIntegerToString() {
|
||||
parseCheck("{1:'January',2:'February',3:'March'}");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalHex01() {
|
||||
parseCheck("0x7FFFFFFF", "2147483647");
|
||||
@Nested
|
||||
class MethodsConstructorsAndArrays {
|
||||
|
||||
@Test
|
||||
void methods() {
|
||||
parseCheck("echo()");
|
||||
parseCheck("echo(12)");
|
||||
parseCheck("echo(name)");
|
||||
parseCheck("echo('Jane')");
|
||||
parseCheck("echo('Jane',32)");
|
||||
parseCheck("echo('Jane', 32)", "echo('Jane',32)");
|
||||
parseCheck("age.doubleItAndAdd(12)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWithNoArguments() {
|
||||
parseCheck("new Foo()");
|
||||
parseCheck("new example.Foo()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWithOneArgument() {
|
||||
parseCheck("new String('hello')");
|
||||
parseCheck("new String( 'hello' )", "new String('hello')");
|
||||
parseCheck("new String(\"hello\" )", "new String('hello')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWithMultipleArguments() {
|
||||
parseCheck("new example.Person('Jane',32,true)");
|
||||
parseCheck("new example.Person('Jane', 32, true)", "new example.Person('Jane',32,true)");
|
||||
parseCheck("new example.Person('Jane', 2 * 16, true)", "new example.Person('Jane',(2 * 16),true)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayConstructionWithOneDimensionalReferenceType() {
|
||||
parseCheck("new String[3]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayConstructionWithOneDimensionalFullyQualifiedReferenceType() {
|
||||
parseCheck("new java.lang.String[3]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayConstructionWithOneDimensionalPrimitiveType() {
|
||||
parseCheck("new int[3]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayConstructionWithMultiDimensionalReferenceType() {
|
||||
parseCheck("new Float[3][4]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayConstructionWithMultiDimensionalPrimitiveType() {
|
||||
parseCheck("new int[3][4]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayConstructionWithOneDimensionalReferenceTypeWithInitializer() {
|
||||
parseCheck("new String[] {'abc','xyz'}");
|
||||
parseCheck("new String[] {'abc', 'xyz'}", "new String[] {'abc','xyz'}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayConstructionWithOneDimensionalPrimitiveTypeWithInitializer() {
|
||||
parseCheck("new int[] {1,2,3,4,5}");
|
||||
parseCheck("new int[] {1, 2, 3, 4, 5}", "new int[] {1,2,3,4,5}");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalDate01() {
|
||||
parseCheck("date('1974/08/24')");
|
||||
@Nested
|
||||
class VariablesAndFunctions {
|
||||
|
||||
@Test
|
||||
void variables() {
|
||||
parseCheck("#foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void functions() {
|
||||
parseCheck("#fn(1,2,3)");
|
||||
parseCheck("#fn('hello')");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalDate02() {
|
||||
parseCheck("date('19740824T131030','yyyyMMddTHHmmss')");
|
||||
@Nested
|
||||
class ElvisAndTernaryOperators {
|
||||
|
||||
@Test
|
||||
void elvis() {
|
||||
parseCheck("3?:1", "(3 ?: 1)");
|
||||
parseCheck("(2*3)?:1*10", "((2 * 3) ?: (1 * 10))");
|
||||
parseCheck("((2*3)?:1)*10", "(((2 * 3) ?: 1) * 10)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ternary() {
|
||||
parseCheck("1>2?3:4", "((1 > 2) ? 3 : 4)");
|
||||
parseCheck("(a ? 1 : 0) * 10", "((a ? 1 : 0) * 10)");
|
||||
parseCheck("(a?1:0)*10", "((a ? 1 : 0) * 10)");
|
||||
parseCheck("(4 % 2 == 0 ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
|
||||
parseCheck("((4 % 2 == 0) ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
|
||||
parseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'",
|
||||
"(({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd')");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalNull01() {
|
||||
parseCheck("null");
|
||||
@Nested
|
||||
class TypeReferences {
|
||||
|
||||
@Test
|
||||
void typeReferences01() {
|
||||
parseCheck("T(java.lang.String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeReferences02() {
|
||||
parseCheck("T(String)");
|
||||
}
|
||||
}
|
||||
|
||||
// boolean operators
|
||||
@Test
|
||||
void booleanOperatorsOr01() {
|
||||
parseCheck("false or false", "(false or false)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanOperatorsOr02() {
|
||||
parseCheck("false or true", "(false or true)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanOperatorsOr03() {
|
||||
parseCheck("true or false", "(true or false)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanOperatorsOr04() {
|
||||
parseCheck("true or false", "(true or false)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanOperatorsMix01() {
|
||||
parseCheck("false or true and false", "(false or (true and false))");
|
||||
}
|
||||
|
||||
// relational operators
|
||||
@Test
|
||||
void relOperatorsGT01() {
|
||||
parseCheck("3>6", "(3 > 6)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsLT01() {
|
||||
parseCheck("3<6", "(3 < 6)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsLE01() {
|
||||
parseCheck("3<=6", "(3 <= 6)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsGE01() {
|
||||
parseCheck("3>=6", "(3 >= 6)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void relOperatorsGE02() {
|
||||
parseCheck("3>=3", "(3 >= 3)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void elvis() {
|
||||
parseCheck("3?:1", "(3 ?: 1)");
|
||||
parseCheck("(2*3)?:1*10", "((2 * 3) ?: (1 * 10))");
|
||||
parseCheck("((2*3)?:1)*10", "(((2 * 3) ?: 1) * 10)");
|
||||
}
|
||||
|
||||
// void relOperatorsIn01() {
|
||||
// parseCheck("3 in {1,2,3,4,5}", "(3 in {1,2,3,4,5})");
|
||||
// }
|
||||
//
|
||||
// void relOperatorsBetween01() {
|
||||
// parseCheck("1 between {1, 5}", "(1 between {1,5})");
|
||||
// }
|
||||
|
||||
// void relOperatorsBetween02() {
|
||||
// parseCheck("'efg' between {'abc', 'xyz'}", "('efg' between {'abc','xyz'})");
|
||||
// }// true
|
||||
|
||||
@Test
|
||||
void relOperatorsIs01() {
|
||||
parseCheck("'xyz' instanceof int", "('xyz' instanceof int)");
|
||||
}// false
|
||||
|
||||
// void relOperatorsIs02() {
|
||||
// parseCheck("{1, 2, 3, 4, 5} instanceof List", "({1,2,3,4,5} instanceof List)");
|
||||
// }// true
|
||||
|
||||
@Test
|
||||
void relOperatorsMatches01() {
|
||||
parseCheck("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "('5.0067' matches '^-?\\d+(\\.\\d{2})?$')");
|
||||
}// false
|
||||
|
||||
@Test
|
||||
void relOperatorsMatches02() {
|
||||
parseCheck("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "('5.00' matches '^-?\\d+(\\.\\d{2})?$')");
|
||||
}// true
|
||||
|
||||
// mathematical operators
|
||||
@Test
|
||||
void mathOperatorsAdd01() {
|
||||
parseCheck("2+4", "(2 + 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsAdd02() {
|
||||
parseCheck("'a'+'b'", "('a' + 'b')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsAdd03() {
|
||||
parseCheck("'hello'+' '+'world'", "(('hello' + ' ') + 'world')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsSubtract01() {
|
||||
parseCheck("5-4", "(5 - 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsMultiply01() {
|
||||
parseCheck("7*4", "(7 * 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorsDivide01() {
|
||||
parseCheck("8/4", "(8 / 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mathOperatorModulus01() {
|
||||
parseCheck("7 % 4", "(7 % 4)");
|
||||
}
|
||||
|
||||
// mixed operators
|
||||
@Test
|
||||
void mixedOperators01() {
|
||||
parseCheck("true and 5>3", "(true and (5 > 3))");
|
||||
}
|
||||
|
||||
// collection processors
|
||||
// void collectionProcessorsCount01() {
|
||||
// parseCheck("new String[] {'abc','def','xyz'}.count()");
|
||||
// }
|
||||
|
||||
// void collectionProcessorsCount02() {
|
||||
// parseCheck("new int[] {1,2,3}.count()");
|
||||
// }
|
||||
//
|
||||
// void collectionProcessorsMax01() {
|
||||
// parseCheck("new int[] {1,2,3}.max()");
|
||||
// }
|
||||
//
|
||||
// void collectionProcessorsMin01() {
|
||||
// parseCheck("new int[] {1,2,3}.min()");
|
||||
// }
|
||||
//
|
||||
// void collectionProcessorsAverage01() {
|
||||
// parseCheck("new int[] {1,2,3}.average()");
|
||||
// }
|
||||
//
|
||||
// void collectionProcessorsSort01() {
|
||||
// parseCheck("new int[] {3,2,1}.sort()");
|
||||
// }
|
||||
//
|
||||
// void collectionProcessorsNonNull01() {
|
||||
// parseCheck("{'a','b',null,'d',null}.nonNull()");
|
||||
// }
|
||||
//
|
||||
// void collectionProcessorsDistinct01() {
|
||||
// parseCheck("{'a','b','a','d','e'}.distinct()");
|
||||
// }
|
||||
|
||||
// references
|
||||
@Test
|
||||
void references01() {
|
||||
parseCheck("@foo");
|
||||
parseCheck("@'foo.bar'");
|
||||
parseCheck("@\"foo.bar.goo\"" , "@'foo.bar.goo'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void references03() {
|
||||
parseCheck("@$$foo");
|
||||
}
|
||||
|
||||
// properties
|
||||
@Test
|
||||
void properties01() {
|
||||
parseCheck("name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void properties02() {
|
||||
parseCheck("placeofbirth.CitY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void properties03() {
|
||||
parseCheck("a.b.c.d.e");
|
||||
}
|
||||
|
||||
// inline list creation
|
||||
@Test
|
||||
void inlineListCreation01() {
|
||||
parseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void inlineListCreation02() {
|
||||
parseCheck("{'abc','xyz'}", "{'abc','xyz'}");
|
||||
}
|
||||
|
||||
// inline map creation
|
||||
@Test
|
||||
void inlineMapCreation01() {
|
||||
parseCheck("{'key1':'Value 1','today':DateTime.Today}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void inlineMapCreation02() {
|
||||
parseCheck("{1:'January',2:'February',3:'March'}");
|
||||
}
|
||||
|
||||
// methods
|
||||
@Test
|
||||
void methods01() {
|
||||
parseCheck("echo(12)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void methods02() {
|
||||
parseCheck("echo(name)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void methods03() {
|
||||
parseCheck("age.doubleItAndAdd(12)");
|
||||
}
|
||||
|
||||
// constructors
|
||||
@Test
|
||||
void constructors01() {
|
||||
parseCheck("new String('hello')");
|
||||
}
|
||||
|
||||
// void constructors02() {
|
||||
// parseCheck("new String[3]");
|
||||
// }
|
||||
|
||||
// array construction
|
||||
// void arrayConstruction01() {
|
||||
// parseCheck("new int[] {1, 2, 3, 4, 5}", "new int[] {1,2,3,4,5}");
|
||||
// }
|
||||
//
|
||||
// void arrayConstruction02() {
|
||||
// parseCheck("new String[] {'abc','xyz'}", "new String[] {'abc','xyz'}");
|
||||
// }
|
||||
|
||||
// variables and functions
|
||||
@Test
|
||||
void variables01() {
|
||||
parseCheck("#foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void functions01() {
|
||||
parseCheck("#fn(1,2,3)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void functions02() {
|
||||
parseCheck("#fn('hello')");
|
||||
}
|
||||
|
||||
// projections and selections
|
||||
// void projections01() {
|
||||
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.!{#isEven()}");
|
||||
// }
|
||||
|
||||
// void selections01() {
|
||||
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}",
|
||||
// "{1,2,3,4,5,6,7,8,9,10}.?{(#isEven(#this) == 'y')}");
|
||||
// }
|
||||
|
||||
// void selectionsFirst01() {
|
||||
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.^{#isEven(#this) == 'y'}",
|
||||
// "{1,2,3,4,5,6,7,8,9,10}.^{(#isEven(#this) == 'y')}");
|
||||
// }
|
||||
|
||||
// void selectionsLast01() {
|
||||
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.${#isEven(#this) == 'y'}",
|
||||
// "{1,2,3,4,5,6,7,8,9,10}.${(#isEven(#this) == 'y')}");
|
||||
// }
|
||||
|
||||
// assignment
|
||||
@Test
|
||||
void assignmentToVariables01() {
|
||||
parseCheck("#var1='value1'");
|
||||
}
|
||||
|
||||
|
||||
// ternary operator
|
||||
|
||||
@Test
|
||||
void ternaryOperator01() {
|
||||
parseCheck("1>2?3:4", "((1 > 2) ? 3 : 4)");
|
||||
parseCheck("(a ? 1 : 0) * 10", "((a ? 1 : 0) * 10)");
|
||||
parseCheck("(a?1:0)*10", "((a ? 1 : 0) * 10)");
|
||||
parseCheck("(4 % 2 == 0 ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
|
||||
parseCheck("((4 % 2 == 0) ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ternaryOperator02() {
|
||||
parseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'",
|
||||
"(({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd')");
|
||||
}
|
||||
|
||||
//
|
||||
// void lambdaMax() {
|
||||
// parseCheck("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", "(#max={|x,y| ($x > $y) ? $x : $y };#max(5,25))");
|
||||
// }
|
||||
//
|
||||
// void lambdaFactorial() {
|
||||
// parseCheck("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))",
|
||||
// "(#fact={|n| ($n <= 1) ? 1 : ($n * #fact(($n - 1))) };#fact(5))");
|
||||
// } // 120
|
||||
|
||||
// Type references
|
||||
@Test
|
||||
void typeReferences01() {
|
||||
parseCheck("T(java.lang.String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeReferences02() {
|
||||
parseCheck("T(String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void inlineList1() {
|
||||
parseCheck("{1,2,3,4}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the supplied expression and then create a string representation of the resultant AST, it should be the same
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+2
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -209,7 +209,7 @@ class SpelReproTests extends AbstractExpressionTests {
|
||||
checkTemplateParsingError("abc${ } }", "No expression defined within delimiter '${}' at character 3");
|
||||
checkTemplateParsingError("abc$[ } ]", DOLLARSQUARE_TEMPLATE_PARSER_CONTEXT, "Found closing '}' at position 6 without an opening '{'");
|
||||
|
||||
checkTemplateParsing("abc ${\"def''g}hi\"} jkl", "abc def'g}hi jkl");
|
||||
checkTemplateParsing("abc ${\"def''g}hi\"} jkl", "abc def''g}hi jkl");
|
||||
checkTemplateParsing("abc ${'def''g}hi'} jkl", "abc def'g}hi jkl");
|
||||
checkTemplateParsing("}", "}");
|
||||
checkTemplateParsing("${'hello'} world", "hello world");
|
||||
@@ -1817,7 +1817,6 @@ class SpelReproTests extends AbstractExpressionTests {
|
||||
static class CCC {
|
||||
|
||||
public boolean method(Object o) {
|
||||
System.out.println(o);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1889,7 +1888,6 @@ class SpelReproTests extends AbstractExpressionTests {
|
||||
static class Foo2 {
|
||||
|
||||
public void execute(String str) {
|
||||
System.out.println("Value: " + str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1964,7 +1962,6 @@ class SpelReproTests extends AbstractExpressionTests {
|
||||
public static class ReflectionUtil<T extends Number> {
|
||||
|
||||
public Object methodToCall(T param) {
|
||||
System.out.println(param + " " + param.getClass());
|
||||
return "Object methodToCall(T param)";
|
||||
}
|
||||
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.expression.spel;
|
||||
|
||||
import org.junit.platform.suite.api.IncludeClassNamePatterns;
|
||||
import org.junit.platform.suite.api.SelectPackages;
|
||||
import org.junit.platform.suite.api.Suite;
|
||||
|
||||
/**
|
||||
* JUnit Platform based test suite for tests in the spring-expression module.
|
||||
*
|
||||
* <p><strong>This suite is only intended to be used manually within an IDE.</strong>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
@Suite
|
||||
@SelectPackages("org.springframework.expression.spel")
|
||||
@IncludeClassNamePatterns(".*Tests?$")
|
||||
class SpringExpressionTestSuite {
|
||||
}
|
||||
+19
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,13 +19,17 @@ package org.springframework.expression.spel.support;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.MethodExecutor;
|
||||
import org.springframework.expression.MethodResolver;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
@@ -364,6 +368,20 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
|
||||
field.write(ctx, tester, "field", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void reflectiveMethodResolverForJdkProxies() throws Exception {
|
||||
Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { Runnable.class }, (p, m, args) -> null);
|
||||
|
||||
MethodResolver resolver = new ReflectiveMethodResolver();
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||
|
||||
MethodExecutor bogus = resolver.resolve(evaluationContext, proxy, "bogus", Collections.emptyList());
|
||||
assertThat(bogus).as("MethodExecutor for bogus()").isNull();
|
||||
MethodExecutor toString = resolver.resolve(evaluationContext, proxy, "toString", Collections.emptyList());
|
||||
assertThat(toString).as("MethodExecutor for toString()").isNotNull();
|
||||
MethodExecutor hashCode = resolver.resolve(evaluationContext, proxy, "hashCode", Collections.emptyList());
|
||||
assertThat(hashCode).as("MethodExecutor for hashCode()").isNotNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to validate the match returned from a compareArguments call.
|
||||
|
||||
+74
-50
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,31 +48,42 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* {@link RowMapper} implementation that converts a row into a new instance
|
||||
* of the specified mapped target class. The mapped target class must be a
|
||||
* top-level class and it must have a default or no-arg constructor.
|
||||
* top-level class or {@code static} nested class, and it must have a default or
|
||||
* no-arg constructor.
|
||||
*
|
||||
* <p>Column values are mapped based on matching the column name as obtained from result set
|
||||
* meta-data to public setters for the corresponding properties. The names are matched either
|
||||
* directly or by transforming a name separating the parts with underscores to the same name
|
||||
* using "camel" case.
|
||||
* <p>Column values are mapped based on matching the column name (as obtained from
|
||||
* result set meta-data) to public setters in the target class for the corresponding
|
||||
* properties. The names are matched either directly or by transforming a name
|
||||
* separating the parts with underscores to the same name using "camel" case.
|
||||
*
|
||||
* <p>Mapping is provided for fields in the target class for many common types, e.g.:
|
||||
* String, boolean, Boolean, byte, Byte, short, Short, int, Integer, long, Long,
|
||||
* float, Float, double, Double, BigDecimal, {@code java.util.Date}, etc.
|
||||
* <p>Mapping is provided for properties in the target class for many common types —
|
||||
* for example: String, boolean, Boolean, byte, Byte, short, Short, int, Integer,
|
||||
* long, Long, float, Float, double, Double, BigDecimal, {@code java.util.Date}, etc.
|
||||
*
|
||||
* <p>To facilitate mapping between columns and fields that don't have matching names,
|
||||
* try using column aliases in the SQL statement like "select fname as first_name from customer".
|
||||
* <p>To facilitate mapping between columns and properties that don't have matching
|
||||
* names, try using column aliases in the SQL statement like
|
||||
* {@code "select fname as first_name from customer"}, where {@code first_name}
|
||||
* can be mapped to a {@code setFirstName(String)} method in the target class.
|
||||
*
|
||||
* <p>For 'null' values read from the database, we will attempt to call the setter, but in the case of
|
||||
* Java primitives, this causes a TypeMismatchException. This class can be configured (using the
|
||||
* primitivesDefaultedForNullValue property) to trap this exception and use the primitives default value.
|
||||
* Be aware that if you use the values from the generated bean to update the database the primitive value
|
||||
* will have been set to the primitive's default value instead of null.
|
||||
* <p>For a {@code NULL} value read from the database, an attempt will be made to
|
||||
* call the corresponding setter method with {@code null}, but in the case of
|
||||
* Java primitives this will result in a {@link TypeMismatchException} by default.
|
||||
* To ignore {@code NULL} database values for all primitive properties in the
|
||||
* target class, set the {@code primitivesDefaultedForNullValue} flag to
|
||||
* {@code true}. See {@link #setPrimitivesDefaultedForNullValue(boolean)} for
|
||||
* details.
|
||||
*
|
||||
* <p>Please note that this class is designed to provide convenience rather than high performance.
|
||||
* For best performance, consider using a custom {@link RowMapper} implementation.
|
||||
* <p>If you need to map to a target class which has a <em>data class</em> constructor
|
||||
* — for example, a Java {@code record} or a Kotlin {@code data} class —
|
||||
* use {@link DataClassRowMapper} instead.
|
||||
*
|
||||
* <p>Please note that this class is designed to provide convenience rather than
|
||||
* high performance. For best performance, consider using a custom {@code RowMapper}
|
||||
* implementation.
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @param <T> the result type
|
||||
* @see DataClassRowMapper
|
||||
@@ -89,20 +100,24 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||
/** Whether we're strictly validating. */
|
||||
private boolean checkFullyPopulated = false;
|
||||
|
||||
/** Whether we're defaulting primitives when mapping a null value. */
|
||||
/**
|
||||
* Whether {@code NULL} database values should be ignored for primitive
|
||||
* properties in the target class.
|
||||
* @see #setPrimitivesDefaultedForNullValue(boolean)
|
||||
*/
|
||||
private boolean primitivesDefaultedForNullValue = false;
|
||||
|
||||
/** ConversionService for binding JDBC values to bean properties. */
|
||||
@Nullable
|
||||
private ConversionService conversionService = DefaultConversionService.getSharedInstance();
|
||||
|
||||
/** Map of the fields we provide mapping for. */
|
||||
/** Map of the properties we provide mapping for. */
|
||||
@Nullable
|
||||
private Map<String, PropertyDescriptor> mappedFields;
|
||||
private Map<String, PropertyDescriptor> mappedProperties;
|
||||
|
||||
/** Set of bean properties we provide mapping for. */
|
||||
/** Set of bean property names we provide mapping for. */
|
||||
@Nullable
|
||||
private Set<String> mappedProperties;
|
||||
private Set<String> mappedPropertyNames;
|
||||
|
||||
|
||||
/**
|
||||
@@ -126,7 +141,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||
* Create a new {@code BeanPropertyRowMapper}.
|
||||
* @param mappedClass the class that each row should be mapped to
|
||||
* @param checkFullyPopulated whether we're strictly validating that
|
||||
* all bean properties have been mapped from corresponding database fields
|
||||
* all bean properties have been mapped from corresponding database columns
|
||||
*/
|
||||
public BeanPropertyRowMapper(Class<T> mappedClass, boolean checkFullyPopulated) {
|
||||
initialize(mappedClass);
|
||||
@@ -159,7 +174,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
/**
|
||||
* Set whether we're strictly validating that all bean properties have been mapped
|
||||
* from corresponding database fields.
|
||||
* from corresponding database columns.
|
||||
* <p>Default is {@code false}, accepting unpopulated properties in the target bean.
|
||||
*/
|
||||
public void setCheckFullyPopulated(boolean checkFullyPopulated) {
|
||||
@@ -168,24 +183,33 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
/**
|
||||
* Return whether we're strictly validating that all bean properties have been
|
||||
* mapped from corresponding database fields.
|
||||
* mapped from corresponding database columns.
|
||||
*/
|
||||
public boolean isCheckFullyPopulated() {
|
||||
return this.checkFullyPopulated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether we're defaulting Java primitives in the case of mapping a null value
|
||||
* from corresponding database fields.
|
||||
* <p>Default is {@code false}, throwing an exception when nulls are mapped to Java primitives.
|
||||
* Set whether a {@code NULL} database column value should be ignored when
|
||||
* mapping to a corresponding primitive property in the target class.
|
||||
* <p>Default is {@code false}, throwing an exception when nulls are mapped
|
||||
* to Java primitives.
|
||||
* <p>If this flag is set to {@code true} and you use an <em>ignored</em>
|
||||
* primitive property value from the mapped bean to update the database, the
|
||||
* value in the database will be changed from {@code NULL} to the current value
|
||||
* of that primitive property. That value may be the property's initial value
|
||||
* (potentially Java's default value for the respective primitive type), or
|
||||
* it may be some other value set for the property in the default constructor
|
||||
* (or initialization block) or as a side effect of setting some other property
|
||||
* in the mapped bean.
|
||||
*/
|
||||
public void setPrimitivesDefaultedForNullValue(boolean primitivesDefaultedForNullValue) {
|
||||
this.primitivesDefaultedForNullValue = primitivesDefaultedForNullValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether we're defaulting Java primitives in the case of mapping a null value
|
||||
* from corresponding database fields.
|
||||
* Get the value of the {@code primitivesDefaultedForNullValue} flag.
|
||||
* @see #setPrimitivesDefaultedForNullValue(boolean)
|
||||
*/
|
||||
public boolean isPrimitivesDefaultedForNullValue() {
|
||||
return this.primitivesDefaultedForNullValue;
|
||||
@@ -220,31 +244,31 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||
*/
|
||||
protected void initialize(Class<T> mappedClass) {
|
||||
this.mappedClass = mappedClass;
|
||||
this.mappedFields = new HashMap<>();
|
||||
this.mappedProperties = new HashSet<>();
|
||||
this.mappedProperties = new HashMap<>();
|
||||
this.mappedPropertyNames = new HashSet<>();
|
||||
|
||||
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(mappedClass)) {
|
||||
if (pd.getWriteMethod() != null) {
|
||||
String lowerCaseName = lowerCaseName(pd.getName());
|
||||
this.mappedFields.put(lowerCaseName, pd);
|
||||
this.mappedProperties.put(lowerCaseName, pd);
|
||||
String underscoreName = underscoreName(pd.getName());
|
||||
if (!lowerCaseName.equals(underscoreName)) {
|
||||
this.mappedFields.put(underscoreName, pd);
|
||||
this.mappedProperties.put(underscoreName, pd);
|
||||
}
|
||||
this.mappedProperties.add(pd.getName());
|
||||
this.mappedPropertyNames.add(pd.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified property from the mapped fields.
|
||||
* Remove the specified property from the mapped properties.
|
||||
* @param propertyName the property name (as used by property descriptors)
|
||||
* @since 5.3.9
|
||||
*/
|
||||
protected void suppressProperty(String propertyName) {
|
||||
if (this.mappedFields != null) {
|
||||
this.mappedFields.remove(lowerCaseName(propertyName));
|
||||
this.mappedFields.remove(underscoreName(propertyName));
|
||||
if (this.mappedProperties != null) {
|
||||
this.mappedProperties.remove(lowerCaseName(propertyName));
|
||||
this.mappedProperties.remove(underscoreName(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,8 +330,8 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
for (int index = 1; index <= columnCount; index++) {
|
||||
String column = JdbcUtils.lookupColumnName(rsmd, index);
|
||||
String field = lowerCaseName(StringUtils.delete(column, " "));
|
||||
PropertyDescriptor pd = (this.mappedFields != null ? this.mappedFields.get(field) : null);
|
||||
String property = lowerCaseName(StringUtils.delete(column, " "));
|
||||
PropertyDescriptor pd = (this.mappedProperties != null ? this.mappedProperties.get(property) : null);
|
||||
if (pd != null) {
|
||||
try {
|
||||
Object value = getColumnValue(rs, index, pd);
|
||||
@@ -321,11 +345,11 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||
catch (TypeMismatchException ex) {
|
||||
if (value == null && this.primitivesDefaultedForNullValue) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
|
||||
" and column '" + column + "' with null value when setting property '" +
|
||||
pd.getName() + "' of type '" +
|
||||
ClassUtils.getQualifiedName(pd.getPropertyType()) +
|
||||
"' on object: " + mappedObject, ex);
|
||||
String propertyType = ClassUtils.getQualifiedName(pd.getPropertyType());
|
||||
logger.debug(String.format(
|
||||
"Ignoring intercepted TypeMismatchException for row %d and column '%s' " +
|
||||
"with null value when setting property '%s' of type '%s' on object: %s",
|
||||
rowNumber, column, pd.getName(), propertyType, mappedObject), ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -343,9 +367,9 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||
}
|
||||
}
|
||||
|
||||
if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
|
||||
throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " +
|
||||
"necessary to populate object of " + this.mappedClass + ": " + this.mappedProperties);
|
||||
if (populatedProperties != null && !populatedProperties.equals(this.mappedPropertyNames)) {
|
||||
throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all properties " +
|
||||
"necessary to populate object of " + this.mappedClass + ": " + this.mappedPropertyNames);
|
||||
}
|
||||
|
||||
return mappedObject;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,15 +31,30 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* {@link RowMapper} implementation that converts a row into a new instance
|
||||
* of the specified mapped target class. The mapped target class must be a
|
||||
* top-level class and may either expose a data class constructor with named
|
||||
* parameters corresponding to column names or classic bean property setters
|
||||
* (or even a combination of both).
|
||||
* top-level class or {@code static} nested class, and it may expose either a
|
||||
* <em>data class</em> constructor with named parameters corresponding to column
|
||||
* names or classic bean property setter methods with property names corresponding
|
||||
* to column names (or even a combination of both).
|
||||
*
|
||||
* <p>The term "data class" applies to Java <em>records</em>, Kotlin <em>data
|
||||
* classes</em>, and any class which has a constructor with named parameters
|
||||
* that are intended to be mapped to corresponding column names.
|
||||
*
|
||||
* <p>When combining a data class constructor with setter methods, any property
|
||||
* mapped successfully via a constructor argument will not be mapped additionally
|
||||
* via a corresponding setter method. This means that constructor arguments take
|
||||
* precedence over property setter methods.
|
||||
*
|
||||
* <p>Note that this class extends {@link BeanPropertyRowMapper} and can
|
||||
* therefore serve as a common choice for any mapped target class, flexibly
|
||||
* adapting to constructor style versus setter methods in the mapped class.
|
||||
*
|
||||
* <p>Please note that this class is designed to provide convenience rather than
|
||||
* high performance. For best performance, consider using a custom {@code RowMapper}
|
||||
* implementation.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 5.3
|
||||
* @param <T> the result type
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,21 +24,21 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* An interface used by {@link JdbcTemplate} for mapping rows of a
|
||||
* {@link java.sql.ResultSet} on a per-row basis. Implementations of this
|
||||
* interface perform the actual work of mapping each row to a result object,
|
||||
* interface perform the actual work of mapping each row to a result object
|
||||
* but don't need to worry about exception handling.
|
||||
* {@link java.sql.SQLException SQLExceptions} will be caught and handled
|
||||
* by the calling JdbcTemplate.
|
||||
* by the calling {@code JdbcTemplate}.
|
||||
*
|
||||
* <p>Typically used either for {@link JdbcTemplate}'s query methods
|
||||
* or for out parameters of stored procedures. RowMapper objects are
|
||||
* <p>Typically used either for {@code JdbcTemplate}'s query methods or for
|
||||
* {@code out} parameters of stored procedures. {@code RowMapper} objects are
|
||||
* typically stateless and thus reusable; they are an ideal choice for
|
||||
* implementing row-mapping logic in a single place.
|
||||
*
|
||||
* <p>Alternatively, consider subclassing
|
||||
* {@link org.springframework.jdbc.object.MappingSqlQuery} from the
|
||||
* {@code jdbc.object} package: Instead of working with separate
|
||||
* JdbcTemplate and RowMapper objects, you can build executable query
|
||||
* objects (containing row-mapping logic) in that style.
|
||||
* {@code jdbc.object} package: instead of working with separate
|
||||
* {@code JdbcTemplate} and {@code RowMapper} objects, you can build executable
|
||||
* query objects (containing row-mapping logic) in that style.
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @author Juergen Hoeller
|
||||
@@ -52,13 +52,13 @@ import org.springframework.lang.Nullable;
|
||||
public interface RowMapper<T> {
|
||||
|
||||
/**
|
||||
* Implementations must implement this method to map each row of data
|
||||
* in the ResultSet. This method should not call {@code next()} on
|
||||
* the ResultSet; it is only supposed to map values of the current row.
|
||||
* @param rs the ResultSet to map (pre-initialized for the current row)
|
||||
* Implementations must implement this method to map each row of data in the
|
||||
* {@code ResultSet}. This method should not call {@code next()} on the
|
||||
* {@code ResultSet}; it is only supposed to map values of the current row.
|
||||
* @param rs the {@code ResultSet} to map (pre-initialized for the current row)
|
||||
* @param rowNum the number of the current row
|
||||
* @return the result object for the current row (may be {@code null})
|
||||
* @throws SQLException if an SQLException is encountered getting
|
||||
* @throws SQLException if an SQLException is encountered while getting
|
||||
* column values (that is, there's no need to catch SQLException)
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,15 +30,15 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Object to represent an SQL BLOB/CLOB value parameter. BLOBs can either be an
|
||||
* InputStream or a byte array. CLOBs can be in the form of a Reader, InputStream
|
||||
* InputStream or a byte array. CLOBs can be in the form of a Reader, InputStream,
|
||||
* or String. Each CLOB/BLOB value will be stored together with its length.
|
||||
* The type is based on which constructor is used. Objects of this class are
|
||||
* immutable except for the LobCreator reference. Use them and discard them.
|
||||
* The type is based on which constructor is used. Instances of this class are
|
||||
* stateful and immutable: use them and discard them.
|
||||
*
|
||||
* <p>This class holds a reference to a LocCreator that must be closed after the
|
||||
* update has completed. This is done via a call to the closeLobCreator method.
|
||||
* All handling of the LobCreator is done by the framework classes that use it -
|
||||
* no need to set or close the LobCreator for end users of this class.
|
||||
* <p>This class holds a reference to a {@link LobCreator} that must be closed after
|
||||
* the update has completed. This is done via a call to the {@link #cleanup()} method.
|
||||
* All handling of the {@code LobCreator} is done by the framework classes that use it -
|
||||
* no need to set or close the {@code LobCreator} for end users of this class.
|
||||
*
|
||||
* <p>A usage example:
|
||||
*
|
||||
@@ -72,8 +72,7 @@ public class SqlLobValue implements DisposableSqlTypeValue {
|
||||
private final int length;
|
||||
|
||||
/**
|
||||
* This contains a reference to the LobCreator - so we can close it
|
||||
* once the update is done.
|
||||
* Reference to the LobCreator - so we can close it once the update is done.
|
||||
*/
|
||||
private final LobCreator lobCreator;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+7
-7
@@ -69,10 +69,10 @@ public class SQLExceptionSubclassTranslator extends AbstractFallbackSQLException
|
||||
if (ex instanceof SQLTransientConnectionException) {
|
||||
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
else if (ex instanceof SQLTransactionRollbackException) {
|
||||
if (ex instanceof SQLTransactionRollbackException) {
|
||||
return new ConcurrencyFailureException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
else if (ex instanceof SQLTimeoutException) {
|
||||
if (ex instanceof SQLTimeoutException) {
|
||||
return new QueryTimeoutException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
}
|
||||
@@ -80,19 +80,19 @@ public class SQLExceptionSubclassTranslator extends AbstractFallbackSQLException
|
||||
if (ex instanceof SQLNonTransientConnectionException) {
|
||||
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
else if (ex instanceof SQLDataException) {
|
||||
if (ex instanceof SQLDataException) {
|
||||
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
else if (ex instanceof SQLIntegrityConstraintViolationException) {
|
||||
if (ex instanceof SQLIntegrityConstraintViolationException) {
|
||||
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
else if (ex instanceof SQLInvalidAuthorizationSpecException) {
|
||||
if (ex instanceof SQLInvalidAuthorizationSpecException) {
|
||||
return new PermissionDeniedDataAccessException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
else if (ex instanceof SQLSyntaxErrorException) {
|
||||
if (ex instanceof SQLSyntaxErrorException) {
|
||||
return new BadSqlGrammarException(task, (sql != null ? sql : ""), ex);
|
||||
}
|
||||
else if (ex instanceof SQLFeatureNotSupportedException) {
|
||||
if (ex instanceof SQLFeatureNotSupportedException) {
|
||||
return new InvalidDataAccessApiUsageException(buildMessage(task, sql, ex), ex);
|
||||
}
|
||||
}
|
||||
|
||||
+37
-34
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.jdbc.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
@@ -44,12 +42,15 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
*/
|
||||
class BeanPropertyRowMapperTests extends AbstractRowMapperTests {
|
||||
|
||||
private static final String SELECT_NULL_AS_AGE = "select null as age from people";
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
void overridingDifferentClassDefinedForMapping() {
|
||||
BeanPropertyRowMapper mapper = new BeanPropertyRowMapper(Person.class);
|
||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(() ->
|
||||
mapper.setMappedClass(Long.class));
|
||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
|
||||
.isThrownBy(() -> mapper.setMappedClass(Long.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -61,104 +62,106 @@ class BeanPropertyRowMapperTests extends AbstractRowMapperTests {
|
||||
@Test
|
||||
void staticQueryWithRowMapper() throws Exception {
|
||||
Mock mock = new Mock();
|
||||
List<Person> result = mock.getJdbcTemplate().query(
|
||||
Person person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birth_date, balance from people",
|
||||
new BeanPropertyRowMapper<>(Person.class));
|
||||
assertThat(result).hasSize(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
void mappingWithInheritance() throws Exception {
|
||||
Mock mock = new Mock();
|
||||
List<ConcretePerson> result = mock.getJdbcTemplate().query(
|
||||
ConcretePerson person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birth_date, balance from people",
|
||||
new BeanPropertyRowMapper<>(ConcretePerson.class));
|
||||
assertThat(result).hasSize(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
void mappingWithNoUnpopulatedFieldsFound() throws Exception {
|
||||
Mock mock = new Mock();
|
||||
List<ConcretePerson> result = mock.getJdbcTemplate().query(
|
||||
ConcretePerson person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birth_date, balance from people",
|
||||
new BeanPropertyRowMapper<>(ConcretePerson.class, true));
|
||||
assertThat(result).hasSize(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
void mappingWithUnpopulatedFieldsNotChecked() throws Exception {
|
||||
Mock mock = new Mock();
|
||||
List<ExtendedPerson> result = mock.getJdbcTemplate().query(
|
||||
ExtendedPerson person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birth_date, balance from people",
|
||||
new BeanPropertyRowMapper<>(ExtendedPerson.class));
|
||||
assertThat(result).hasSize(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
void mappingWithUnpopulatedFieldsNotAccepted() throws Exception {
|
||||
BeanPropertyRowMapper<ExtendedPerson> mapper = new BeanPropertyRowMapper<>(ExtendedPerson.class, true);
|
||||
Mock mock = new Mock();
|
||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(() ->
|
||||
mock.getJdbcTemplate().query("select name, age, birth_date, balance from people",
|
||||
new BeanPropertyRowMapper<>(ExtendedPerson.class, true)));
|
||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
|
||||
.isThrownBy(() -> mock.getJdbcTemplate().query("select name, age, birth_date, balance from people", mapper));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mappingNullValue() throws Exception {
|
||||
BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
||||
Mock mock = new Mock(MockType.TWO);
|
||||
assertThatExceptionOfType(TypeMismatchException.class).isThrownBy(() ->
|
||||
mock.getJdbcTemplate().query("select name, null as age, birth_date, balance from people", mapper));
|
||||
assertThatExceptionOfType(TypeMismatchException.class)
|
||||
.isThrownBy(() -> mock.getJdbcTemplate().query(SELECT_NULL_AS_AGE, mapper));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mappingNullValueWithPrimitivesDefaultedForNullValue() throws Exception {
|
||||
BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
||||
mapper.setPrimitivesDefaultedForNullValue(true);
|
||||
Mock mock = new Mock(MockType.TWO);
|
||||
Person person = mock.getJdbcTemplate().queryForObject(SELECT_NULL_AS_AGE, mapper);
|
||||
assertThat(person).extracting(Person::getAge).isEqualTo(42L);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryWithSpaceInColumnNameAndLocalDateTime() throws Exception {
|
||||
Mock mock = new Mock(MockType.THREE);
|
||||
List<SpacePerson> result = mock.getJdbcTemplate().query(
|
||||
SpacePerson person = mock.getJdbcTemplate().queryForObject(
|
||||
"select last_name as \"Last Name\", age, birth_date, balance from people",
|
||||
new BeanPropertyRowMapper<>(SpacePerson.class));
|
||||
assertThat(result).hasSize(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryWithSpaceInColumnNameAndLocalDate() throws Exception {
|
||||
Mock mock = new Mock(MockType.THREE);
|
||||
List<DatePerson> result = mock.getJdbcTemplate().query(
|
||||
DatePerson person = mock.getJdbcTemplate().queryForObject(
|
||||
"select last_name as \"Last Name\", age, birth_date, balance from people",
|
||||
new BeanPropertyRowMapper<>(DatePerson.class));
|
||||
assertThat(result).hasSize(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryWithDirectNameMatchOnBirthDate() throws Exception {
|
||||
Mock mock = new Mock(MockType.FOUR);
|
||||
List<ConcretePerson> result = mock.getJdbcTemplate().query(
|
||||
ConcretePerson person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birthdate, balance from people",
|
||||
new BeanPropertyRowMapper<>(ConcretePerson.class));
|
||||
assertThat(result).hasSize(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryWithUnderscoreInColumnNameAndPersonWithMultipleAdjacentUppercaseLettersInPropertyName() throws Exception {
|
||||
Mock mock = new Mock();
|
||||
List<EmailPerson> result = mock.getJdbcTemplate().query(
|
||||
EmailPerson person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birth_date, balance, e_mail from people",
|
||||
new BeanPropertyRowMapper<>(EmailPerson.class));
|
||||
assertThat(result).hasSize(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
|
||||
+15
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,9 +17,7 @@
|
||||
package org.springframework.jdbc.core;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -30,50 +28,48 @@ import org.springframework.jdbc.core.test.ConstructorPersonWithSetters;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DataClassRowMapper}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 5.3
|
||||
*/
|
||||
public class DataClassRowMapperTests extends AbstractRowMapperTests {
|
||||
class DataClassRowMapperTests extends AbstractRowMapperTests {
|
||||
|
||||
@Test
|
||||
public void testStaticQueryWithDataClass() throws Exception {
|
||||
void staticQueryWithDataClass() throws Exception {
|
||||
Mock mock = new Mock();
|
||||
List<ConstructorPerson> result = mock.getJdbcTemplate().query(
|
||||
ConstructorPerson person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birth_date, balance from people",
|
||||
new DataClassRowMapper<>(ConstructorPerson.class));
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
verifyPerson(result.get(0));
|
||||
verifyPerson(person);
|
||||
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticQueryWithDataClassAndGenerics() throws Exception {
|
||||
void staticQueryWithDataClassAndGenerics() throws Exception {
|
||||
Mock mock = new Mock();
|
||||
List<ConstructorPersonWithGenerics> result = mock.getJdbcTemplate().query(
|
||||
ConstructorPersonWithGenerics person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birth_date, balance from people",
|
||||
new DataClassRowMapper<>(ConstructorPersonWithGenerics.class));
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
ConstructorPersonWithGenerics person = result.get(0);
|
||||
assertThat(person.name()).isEqualTo("Bubba");
|
||||
assertThat(person.age()).isEqualTo(22L);
|
||||
assertThat(person.birthDate()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
|
||||
assertThat(person.balance()).isEqualTo(Collections.singletonList(new BigDecimal("1234.56")));
|
||||
assertThat(person.birthDate()).usingComparator(Date::compareTo).isEqualTo(new Date(1221222L));
|
||||
assertThat(person.balance()).containsExactly(new BigDecimal("1234.56"));
|
||||
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticQueryWithDataClassAndSetters() throws Exception {
|
||||
void staticQueryWithDataClassAndSetters() throws Exception {
|
||||
Mock mock = new Mock(MockType.FOUR);
|
||||
List<ConstructorPersonWithSetters> result = mock.getJdbcTemplate().query(
|
||||
ConstructorPersonWithSetters person = mock.getJdbcTemplate().queryForObject(
|
||||
"select name, age, birthdate, balance from people",
|
||||
new DataClassRowMapper<>(ConstructorPersonWithSetters.class));
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
ConstructorPersonWithSetters person = result.get(0);
|
||||
assertThat(person.name()).isEqualTo("BUBBA");
|
||||
assertThat(person.age()).isEqualTo(22L);
|
||||
assertThat(person.birthDate()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
|
||||
assertThat(person.birthDate()).usingComparator(Date::compareTo).isEqualTo(new Date(1221222L));
|
||||
assertThat(person.balance()).isEqualTo(new BigDecimal("1234.56"));
|
||||
|
||||
mock.verifyClosed();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,6 +31,9 @@ public class Person {
|
||||
|
||||
private BigDecimal balance;
|
||||
|
||||
public Person() {
|
||||
this.age = 42; // custom "default" value for a primitive
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user