Skip to content

Commit 2cde02b

Browse files
abhu85claude
andauthored
fix(dynamodb-enhanced): Fix NPE in EnhancedType for null and wildcard types (#6745)
* fix(dynamodb-enhanced): Fix NPE in ConverterUtils and EnhancedType for null values and wildcard types - Add null checks to ConverterUtils.validateDouble() and validateFloat() to prevent NPE when input is null - Add null check for rawClass in EnhancedType.hashCode() and equals() to support wildcard types (List<?>) - Add comprehensive unit tests for null handling Fixes #6639 Fixes #5890 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(dynamodb-enhanced): Add toString() NPE fix, split out ConverterUtils changes - Add null check in innerToString() for wildcard types (rawClass is null) - Add test for toString() with wildcard types - Remove ConverterUtils changes (to be submitted as separate PR per maintainer request) - Update changelog to reflect EnhancedType-only changes * test: add test for wildcard vs non-wildcard equality comparison Addresses review feedback to cover the ternary logic in equals() when comparing a wildcard type (rawClass=null) against a non-wildcard type (rawClass!=null). Tests both comparison directions. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: abhu85 <abhu85@users.noreply.github.com>
1 parent 0fac95c commit 2cde02b

File tree

3 files changed

+68
-8
lines changed

3 files changed

+68
-8
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "Amazon DynamoDB Enhanced Client",
4+
"contributor": "",
5+
"description": "Fix NullPointerException in `EnhancedType.hashCode()`, `EnhancedType.equals()`, and `EnhancedType.toString()` when using wildcard types."
6+
}

services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/EnhancedType.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -541,14 +541,17 @@ private List<EnhancedType<?>> loadTypeParameters(Type type) {
541541

542542
private StringBuilder innerToString() {
543543
StringBuilder result = new StringBuilder();
544-
result.append(rawClass.getTypeName());
544+
if (isWildcard) {
545+
result.append("?");
546+
} else {
547+
result.append(rawClass.getTypeName());
545548

546-
if (null != rawClassParameters && !rawClassParameters.isEmpty()) {
547-
result.append("<");
548-
result.append(rawClassParameters.stream().map(EnhancedType::innerToString).collect(Collectors.joining(", ")));
549-
result.append(">");
549+
if (null != rawClassParameters && !rawClassParameters.isEmpty()) {
550+
result.append("<");
551+
result.append(rawClassParameters.stream().map(EnhancedType::innerToString).collect(Collectors.joining(", ")));
552+
result.append(">");
553+
}
550554
}
551-
552555
return result;
553556
}
554557

@@ -566,7 +569,7 @@ public boolean equals(Object o) {
566569
if (isWildcard != enhancedType.isWildcard) {
567570
return false;
568571
}
569-
if (!rawClass.equals(enhancedType.rawClass)) {
572+
if (rawClass != null ? !rawClass.equals(enhancedType.rawClass) : enhancedType.rawClass != null) {
570573
return false;
571574
}
572575
if (rawClassParameters != null ? !rawClassParameters.equals(enhancedType.rawClassParameters) :
@@ -584,7 +587,7 @@ public boolean equals(Object o) {
584587
@Override
585588
public int hashCode() {
586589
int result = (isWildcard ? 1 : 0);
587-
result = 31 * result + rawClass.hashCode();
590+
result = 31 * result + (rawClass != null ? rawClass.hashCode() : 0);
588591
result = 31 * result + (rawClassParameters != null ? rawClassParameters.hashCode() : 0);
589592
result = 31 * result + (tableSchema != null ? tableSchema.hashCode() : 0);
590593
result = 31 * result + (documentConfiguration != null ? documentConfiguration.hashCode() : 0);

services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/EnhancedTypeTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,57 @@ public void documentOf_withEnhancedTypeConfiguration() {
239239
assertThat(type.documentConfiguration().get().preserveEmptyObject()).isTrue();
240240
}
241241

242+
@Test
243+
public void wildcardType_hashCode_doesNotRaiseNPE() {
244+
// When using a wildcard type like List<?>, the type parameter is a wildcard
245+
// with rawClass = null. hashCode() should handle this without NPE.
246+
EnhancedType<List<?>> listWithWildcard = new EnhancedType<List<?>>(){};
247+
EnhancedType<?> wildcardParam = listWithWildcard.rawClassParameters().get(0);
248+
249+
// This should not throw NPE
250+
assertThatCode(() -> wildcardParam.hashCode()).doesNotThrowAnyException();
251+
}
252+
253+
@Test
254+
public void wildcardType_equals_handlesNullRawClass() {
255+
// Wildcard types should be comparable via equals without NPE
256+
EnhancedType<List<?>> listWithWildcard1 = new EnhancedType<List<?>>(){};
257+
EnhancedType<List<?>> listWithWildcard2 = new EnhancedType<List<?>>(){};
258+
259+
EnhancedType<?> wildcard1 = listWithWildcard1.rawClassParameters().get(0);
260+
EnhancedType<?> wildcard2 = listWithWildcard2.rawClassParameters().get(0);
261+
262+
// Wildcards should be equal to each other
263+
assertThat(wildcard1).isEqualTo(wildcard2);
264+
assertThat(wildcard1.hashCode()).isEqualTo(wildcard2.hashCode());
265+
}
266+
267+
@Test
268+
public void wildcardType_equals_notEqualToNonWildcard() {
269+
// A wildcard type (rawClass=null) should not be equal to a non-wildcard type (rawClass!=null)
270+
EnhancedType<List<?>> listWithWildcard = new EnhancedType<List<?>>(){};
271+
EnhancedType<?> wildcardType = listWithWildcard.rawClassParameters().get(0);
272+
EnhancedType<String> nonWildcardType = EnhancedType.of(String.class);
273+
274+
// Wildcard vs non-wildcard should not be equal (tests both comparison directions)
275+
assertThat(wildcardType).isNotEqualTo(nonWildcardType);
276+
assertThat(nonWildcardType).isNotEqualTo(wildcardType);
277+
}
278+
279+
@Test
280+
public void wildcardType_toString_doesNotRaiseNPE() {
281+
// Wildcard types should be convertible to string without NPE
282+
EnhancedType<List<?>> listWithWildcard = new EnhancedType<List<?>>(){};
283+
EnhancedType<?> wildcardParam = listWithWildcard.rawClassParameters().get(0);
284+
285+
// This should not throw NPE and should return "?" for wildcard
286+
assertThatCode(() -> wildcardParam.toString()).doesNotThrowAnyException();
287+
assertThat(wildcardParam.toString()).isEqualTo("EnhancedType(?)");
288+
289+
// Also verify that the parent type renders correctly with wildcard
290+
assertThat(listWithWildcard.toString()).isEqualTo("EnhancedType(java.util.List<?>)");
291+
}
292+
242293
public class InnerType {
243294
}
244295

0 commit comments

Comments
 (0)