Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "Amazon DynamoDB Enhanced Client",
"contributor": "",
"description": "Fix NullPointerException in `ConverterUtils.validateDouble` and `ConverterUtils.validateFloat` when input is null, and in `EnhancedType.hashCode()` when using wildcard types."
}
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ public boolean equals(Object o) {
if (isWildcard != enhancedType.isWildcard) {
return false;
}
if (!rawClass.equals(enhancedType.rawClass)) {
if (rawClass != null ? !rawClass.equals(enhancedType.rawClass) : enhancedType.rawClass != null) {
return false;
}
if (rawClassParameters != null ? !rawClassParameters.equals(enhancedType.rawClassParameters) :
Expand All @@ -584,7 +584,7 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
int result = (isWildcard ? 1 : 0);
result = 31 * result + rawClass.hashCode();
result = 31 * result + (rawClass != null ? rawClass.hashCode() : 0);
result = 31 * result + (rawClassParameters != null ? rawClassParameters.hashCode() : 0);
result = 31 * result + (tableSchema != null ? tableSchema.hashCode() : 0);
result = 31 * result + (documentConfiguration != null ? documentConfiguration.hashCode() : 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,24 @@ private ConverterUtils() {

/**
* Validates that a given Double input is a valid double supported by {@link DoubleAttributeConverter}.
* @param input
* @param input the Double value to validate, may be null
*/
public static void validateDouble(Double input) {
if (input == null) {
return;
}
Validate.isTrue(!Double.isNaN(input), "NaN is not supported by the default converters.");
Validate.isTrue(Double.isFinite(input), "Infinite numbers are not supported by the default converters.");
}

/**
* Validates that a given Float input is a valid double supported by {@link FloatAttributeConverter}.
* @param input
* Validates that a given Float input is a valid float supported by {@link FloatAttributeConverter}.
* @param input the Float value to validate, may be null
*/
public static void validateFloat(Float input) {
if (input == null) {
return;
}
Validate.isTrue(!Float.isNaN(input), "NaN is not supported by the default converters.");
Validate.isTrue(Float.isFinite(input), "Infinite numbers are not supported by the default converters.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,31 @@ public void documentOf_withEnhancedTypeConfiguration() {
assertThat(type.documentConfiguration().get().preserveEmptyObject()).isTrue();
}

@Test
public void wildcardType_hashCode_doesNotRaiseNPE() {
// When using a wildcard type like List<?>, the type parameter is a wildcard
// with rawClass = null. hashCode() should handle this without NPE.
EnhancedType<List<?>> listWithWildcard = new EnhancedType<List<?>>(){};
EnhancedType<?> wildcardParam = listWithWildcard.rawClassParameters().get(0);

// This should not throw NPE
assertThatCode(() -> wildcardParam.hashCode()).doesNotThrowAnyException();
}

@Test
public void wildcardType_equals_handlesNullRawClass() {
// Wildcard types should be comparable via equals without NPE
EnhancedType<List<?>> listWithWildcard1 = new EnhancedType<List<?>>(){};
EnhancedType<List<?>> listWithWildcard2 = new EnhancedType<List<?>>(){};

EnhancedType<?> wildcard1 = listWithWildcard1.rawClassParameters().get(0);
EnhancedType<?> wildcard2 = listWithWildcard2.rawClassParameters().get(0);

// Wildcards should be equal to each other
assertThat(wildcard1).isEqualTo(wildcard2);
assertThat(wildcard1.hashCode()).isEqualTo(wildcard2.hashCode());
}

public class InnerType {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.enhanced.dynamodb.internal.converter;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import org.junit.jupiter.api.Test;

class ConverterUtilsTest {

@Test
void validateDouble_withNull_doesNotThrowException() {
assertThatCode(() -> ConverterUtils.validateDouble(null))
.doesNotThrowAnyException();
}

@Test
void validateDouble_withValidValue_doesNotThrowException() {
assertThatCode(() -> ConverterUtils.validateDouble(1.5))
.doesNotThrowAnyException();
}

@Test
void validateDouble_withNaN_throwsException() {
assertThatThrownBy(() -> ConverterUtils.validateDouble(Double.NaN))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("NaN is not supported");
}

@Test
void validateDouble_withPositiveInfinity_throwsException() {
assertThatThrownBy(() -> ConverterUtils.validateDouble(Double.POSITIVE_INFINITY))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Infinite numbers are not supported");
}

@Test
void validateDouble_withNegativeInfinity_throwsException() {
assertThatThrownBy(() -> ConverterUtils.validateDouble(Double.NEGATIVE_INFINITY))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Infinite numbers are not supported");
}

@Test
void validateFloat_withNull_doesNotThrowException() {
assertThatCode(() -> ConverterUtils.validateFloat(null))
.doesNotThrowAnyException();
}

@Test
void validateFloat_withValidValue_doesNotThrowException() {
assertThatCode(() -> ConverterUtils.validateFloat(1.5f))
.doesNotThrowAnyException();
}

@Test
void validateFloat_withNaN_throwsException() {
assertThatThrownBy(() -> ConverterUtils.validateFloat(Float.NaN))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("NaN is not supported");
}

@Test
void validateFloat_withPositiveInfinity_throwsException() {
assertThatThrownBy(() -> ConverterUtils.validateFloat(Float.POSITIVE_INFINITY))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Infinite numbers are not supported");
}

@Test
void validateFloat_withNegativeInfinity_throwsException() {
assertThatThrownBy(() -> ConverterUtils.validateFloat(Float.NEGATIVE_INFINITY))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Infinite numbers are not supported");
}
}