Enforce non-null contract on vector elements.
Also add shortcuts for empty sources and simplify copy calls that do not transform source values. See #3193 Original pull request: #3194
This commit is contained in:
committed by
Mark Paluch
parent
059d09a224
commit
db382681a8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
* Copyright 2025 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,7 +30,7 @@ class DoubleVector implements Vector {
|
||||
|
||||
private final double[] v;
|
||||
|
||||
public DoubleVector(double[] v) {
|
||||
DoubleVector(double[] v) {
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,11 @@ class DoubleVector implements Vector {
|
||||
*/
|
||||
static Vector copy(double[] v) {
|
||||
|
||||
double[] copy = new double[v.length];
|
||||
System.arraycopy(v, 0, copy, 0, copy.length);
|
||||
if (v.length == 0) {
|
||||
return new DoubleVector(new double[0]);
|
||||
}
|
||||
|
||||
return new DoubleVector(copy);
|
||||
return new DoubleVector(Arrays.copyOf(v, v.length));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,6 +51,10 @@ class DoubleVector implements Vector {
|
||||
*/
|
||||
static Vector copy(Collection<? extends Number> v) {
|
||||
|
||||
if (v.isEmpty()) {
|
||||
return new DoubleVector(new double[0]);
|
||||
}
|
||||
|
||||
double[] copy = new double[v.size()];
|
||||
int i = 0;
|
||||
for (Number number : v) {
|
||||
@@ -87,11 +92,7 @@ class DoubleVector implements Vector {
|
||||
|
||||
@Override
|
||||
public double[] toDoubleArray() {
|
||||
|
||||
double[] copy = new double[this.v.length];
|
||||
System.arraycopy(this.v, 0, copy, 0, copy.length);
|
||||
|
||||
return copy;
|
||||
return Arrays.copyOf(this.v, this.v.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
* Copyright 2025 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,7 +30,7 @@ class FloatVector implements Vector {
|
||||
|
||||
private final float[] v;
|
||||
|
||||
public FloatVector(float[] v) {
|
||||
FloatVector(float[] v) {
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,11 @@ class FloatVector implements Vector {
|
||||
*/
|
||||
static Vector copy(float[] v) {
|
||||
|
||||
float[] copy = new float[v.length];
|
||||
System.arraycopy(v, 0, copy, 0, copy.length);
|
||||
if (v.length == 0) {
|
||||
return new FloatVector(new float[0]);
|
||||
}
|
||||
|
||||
return new FloatVector(copy);
|
||||
return new FloatVector(Arrays.copyOf(v, v.length));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,6 +51,10 @@ class FloatVector implements Vector {
|
||||
*/
|
||||
static Vector copy(Collection<? extends Number> v) {
|
||||
|
||||
if (v.isEmpty()) {
|
||||
return new FloatVector(new float[0]);
|
||||
}
|
||||
|
||||
float[] copy = new float[v.size()];
|
||||
int i = 0;
|
||||
for (Number number : v) {
|
||||
@@ -76,11 +81,7 @@ class FloatVector implements Vector {
|
||||
|
||||
@Override
|
||||
public float[] toFloatArray() {
|
||||
|
||||
float[] copy = new float[this.v.length];
|
||||
System.arraycopy(this.v, 0, copy, 0, copy.length);
|
||||
|
||||
return copy;
|
||||
return Arrays.copyOf(this.v, this.v.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
* Copyright 2025 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,6 +18,7 @@ package org.springframework.data.domain;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,9 @@ class NumberVector implements Vector {
|
||||
|
||||
private final Number[] v;
|
||||
|
||||
public NumberVector(Number[] v) {
|
||||
NumberVector(Number[] v) {
|
||||
|
||||
Assert.noNullElements(v, "Vector [v] must not contain null elements");
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
@@ -39,41 +42,39 @@ class NumberVector implements Vector {
|
||||
*/
|
||||
static Vector copy(Number[] v) {
|
||||
|
||||
Number[] copy = new Number[v.length];
|
||||
System.arraycopy(v, 0, copy, 0, copy.length);
|
||||
if (v.length == 0) {
|
||||
return new NumberVector(new Number[0]);
|
||||
}
|
||||
|
||||
return new NumberVector(copy);
|
||||
return new NumberVector(Arrays.copyOf(v, v.length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the given {@link Number} and wrap it within a Vector.
|
||||
*/
|
||||
static Vector copy(Collection<? extends Number> numbers) {
|
||||
static Vector copy(Collection<? extends Number> v) {
|
||||
|
||||
Number[] copy = new Number[numbers.size()];
|
||||
|
||||
int i = 0;
|
||||
for (Number number : numbers) {
|
||||
copy[i++] = number;
|
||||
if (v.isEmpty()) {
|
||||
return new NumberVector(new Number[0]);
|
||||
}
|
||||
|
||||
return new NumberVector(copy);
|
||||
return new NumberVector(v.toArray(Number[]::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Number> getType() {
|
||||
|
||||
Class<?> candidate = null;
|
||||
for (Object val : v) {
|
||||
if (val != null) {
|
||||
if (candidate == null) {
|
||||
candidate = val.getClass();
|
||||
} else if (candidate != val.getClass()) {
|
||||
return Number.class;
|
||||
}
|
||||
if (this.v.length == 0) {
|
||||
return Number.class;
|
||||
}
|
||||
|
||||
Class<? extends Number> candidate = this.v[0].getClass();
|
||||
for (int i = 1; i < this.v.length; i++) {
|
||||
if (candidate != this.v[i].getClass()) {
|
||||
return Number.class;
|
||||
}
|
||||
}
|
||||
return (Class<? extends Number>) candidate;
|
||||
return candidate;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
* Copyright 2025 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.
|
||||
@@ -75,6 +75,9 @@ public interface Vector {
|
||||
static Vector of(Collection<? extends Number> values) {
|
||||
|
||||
Assert.notNull(values, "Vector values must not be null");
|
||||
if(values.isEmpty()) {
|
||||
return NumberVector.copy(new Number[0]);
|
||||
}
|
||||
|
||||
Class<?> cet = CollectionUtils.findCommonElementType(values);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
* Copyright 2025 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 2024 the original author or authors.
|
||||
* Copyright 2025 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.
|
||||
@@ -15,9 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.domain;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -25,12 +28,40 @@ import org.junit.jupiter.api.Test;
|
||||
* Unit tests for {@link NumberVector}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class NumberVectorUnitTests {
|
||||
|
||||
Number[] values = new Number[] { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6f };
|
||||
Number[] floats = new Number[] { (float) 1.1d, (float) 2.2d, (float) 3.3d, (float) 4.4d, (float) 5.5, 6.6 };
|
||||
|
||||
@Test // GH-3193
|
||||
void shouldErrorOnNullElements() {
|
||||
|
||||
List<Long> source = new ArrayList<>(3);
|
||||
source.add(1L);
|
||||
source.add(null);
|
||||
source.add(3L);
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> NumberVector.copy(source));
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> NumberVector.copy(new Number[] { 1L, null, 3L }));
|
||||
}
|
||||
|
||||
@Test // GH-3193
|
||||
void shouldAcceptEmptySource() {
|
||||
|
||||
Vector vector = NumberVector.copy(List.of());
|
||||
|
||||
assertThat(vector.size()).isEqualTo(0);
|
||||
assertThat(vector.getType()).isEqualTo(Number.class);
|
||||
|
||||
vector = NumberVector.copy(new Number[] {});
|
||||
|
||||
assertThat(vector.size()).isEqualTo(0);
|
||||
assertThat(vector.getType()).isEqualTo(Number.class);
|
||||
}
|
||||
|
||||
@Test // GH-3193
|
||||
void shouldCreateVector() {
|
||||
|
||||
@@ -48,6 +79,17 @@ class NumberVectorUnitTests {
|
||||
assertThat(vector.getSource()).isNotSameAs(vector).isEqualTo(values);
|
||||
}
|
||||
|
||||
@Test // GH-3193
|
||||
void shouldFigureOutCommonType() {
|
||||
|
||||
assertThat(NumberVector.copy(List.of()).getType()).isEqualTo(Number.class);
|
||||
assertThat(NumberVector.copy(List.of(1)).getType()).isEqualTo(Integer.class);
|
||||
assertThat(NumberVector.copy(List.of(1L, 2L)).getType()).isEqualTo(Long.class);
|
||||
assertThat(NumberVector.copy(List.of(1F, 2F)).getType()).isEqualTo(Float.class);
|
||||
assertThat(NumberVector.copy(List.of(1D, 2D)).getType()).isEqualTo(Double.class);
|
||||
assertThat(NumberVector.copy(List.of(1D, 2F, 3F)).getType()).isEqualTo(Number.class);
|
||||
}
|
||||
|
||||
@Test // GH-3193
|
||||
void shouldRenderToString() {
|
||||
|
||||
@@ -66,7 +108,7 @@ class NumberVectorUnitTests {
|
||||
}
|
||||
|
||||
@Test // GH-3193
|
||||
void sourceShouldReturnSource() {
|
||||
void sourceShouldReturnSource() { // this one is questionable
|
||||
|
||||
Vector vector = new NumberVector(values);
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2025 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.data.domain;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class VectorUnitTests {
|
||||
|
||||
@Test // GH-3193
|
||||
void staticInitializersErrorOnNull() {
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> Vector.of((double[]) null));
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> Vector.of((float[]) null));
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> Vector.of((Collection<Number>) null));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user