Skip to content

Commit c5e7b64

Browse files
authored
Use actual member name for NULL (#6360)
* Use actual member name for NULL In DynamoDB, the "NULL" member of AttributeValue is customized to "NUL" because "null" is a reserved keyword. This has the effect of generating nul() and getNul() as getters, and nul(Boolean), and setNul(Boolean) as setters for the serializable builder class. If using the serializable builder class to map from a JSON string with the correct property name "NULL", it will not be recognized. To fix this, generate an additional setter with the correct name getNull(). To make this work, `ModifyModelShapeModifier` has been updated with a new option called `alternateBeanPropertyName`. When set, for the modified member, we will generate an additional bean style setter on the model builder using the given property name. * Add codegen test
1 parent 98864cd commit c5e7b64

File tree

15 files changed

+707
-2
lines changed

15 files changed

+707
-2
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",
4+
"contributor": "",
5+
"description": "Add a `setNull(Boolean)` setter for the serializable builder class returned by `AttributeValue.serializableBuilderClass()`. This allows successfully converting from a JSON blob such as `{\"NULL\": true}`."
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/AddShapes.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,14 @@ private MemberModel generateMemberModel(String c2jMemberName, Member c2jMemberDe
204204
memberModel.setRequired(isRequiredMember(c2jMemberName, parentShape));
205205
memberModel.setSynthetic(shape.isSynthetic());
206206

207+
if (c2jMemberDefinition.getAlternateBeanPropertyName() != null) {
208+
String alternatePropertyName = c2jMemberDefinition.getAlternateBeanPropertyName();
209+
210+
String setter = String.format("set%s", alternatePropertyName);
211+
212+
memberModel.setAdditionalBeanStyleSetterName(setter);
213+
}
214+
207215

208216
// Pass the xmlNameSpace from the member reference
209217
if (c2jMemberDefinition.getXmlNamespace() != null) {

codegen/src/main/java/software/amazon/awssdk/codegen/customization/processors/ShapeModifiersProcessor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ private void doModifyShapeMembers(ServiceModel serviceModel, Shape shape, String
224224
member.setDeprecatedMessage(modifyModel.getDeprecatedMessage());
225225
}
226226
}
227+
228+
if (modifyModel.getAlternateBeanPropertyName() != null) {
229+
shape.getMembers().get(memberToModify).setAlternateBeanPropertyName(modifyModel.getAlternateBeanPropertyName());
230+
}
231+
227232
// Currently only supports emitPropertyName which is to rename the member
228233
if (modifyModel.getEmitPropertyName() != null) {
229234
Member member = shape.getMembers().remove(memberToModify);

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/ModifyModelShapeModifier.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ public class ModifyModelShapeModifier {
4949
*/
5050
private String emitEnumValue;
5151

52+
/**
53+
* An additional, alternate name for this member that should additionally be used when generating bean style
54+
* setters/getters for the enclosing shape.
55+
*/
56+
private String alternateBeanPropertyName;
57+
5258
/**
5359
* Emit as a different primitive type. Used by AWS Budget Service to change string
5460
* to BigDecimal (see API-433).
@@ -143,4 +149,12 @@ public void setIgnoreDataTypeConversionFailures(boolean ignoreDataTypeConversion
143149
public boolean isIgnoreDataTypeConversionFailures() {
144150
return ignoreDataTypeConversionFailures;
145151
}
152+
153+
public String getAlternateBeanPropertyName() {
154+
return alternateBeanPropertyName;
155+
}
156+
157+
public void setAlternateBeanPropertyName(String alternateBeanPropertyName) {
158+
this.alternateBeanPropertyName = alternateBeanPropertyName;
159+
}
146160
}

codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/MemberModel.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ public class MemberModel extends DocumentationModel {
9090

9191
private String beanStyleSetterName;
9292

93+
private String additionalBeanStyleSetterName;
94+
9395
private String unionEnumTypeName;
9496

9597
private boolean isJsonValue;
@@ -787,6 +789,19 @@ public boolean ignoreDataTypeConversionFailures() {
787789
return ignoreDataTypeConversionFailures;
788790
}
789791

792+
public String getAdditionalBeanStyleSetterName() {
793+
return additionalBeanStyleSetterName;
794+
}
795+
796+
public void setAdditionalBeanStyleSetterName(String additionalBeanStyleSetterName) {
797+
this.additionalBeanStyleSetterName = additionalBeanStyleSetterName;
798+
}
799+
800+
public MemberModel withAdditionalBeanStyleSetterName(String additionalBeanStyleSetterName) {
801+
setAdditionalBeanStyleSetterName(additionalBeanStyleSetterName);
802+
return this;
803+
}
804+
790805
@Override
791806
public boolean equals(Object o) {
792807
if (o == null || getClass() != o.getClass()) {
@@ -834,6 +849,7 @@ public boolean equals(Object o) {
834849
&& Objects.equals(fluentDeprecatedGetterMethodName, that.fluentDeprecatedGetterMethodName)
835850
&& Objects.equals(fluentDeprecatedSetterMethodName, that.fluentDeprecatedSetterMethodName)
836851
&& Objects.equals(deprecatedBeanStyleSetterMethodName, that.deprecatedBeanStyleSetterMethodName)
852+
&& Objects.equals(additionalBeanStyleSetterName, that.additionalBeanStyleSetterName)
837853
&& Objects.equals(contextParam, that.contextParam);
838854
}
839855

@@ -878,6 +894,8 @@ public int hashCode() {
878894
result = 31 * result + Objects.hashCode(deprecatedBeanStyleSetterMethodName);
879895
result = 31 * result + Objects.hashCode(contextParam);
880896
result = 31 * result + Boolean.hashCode(ignoreDataTypeConversionFailures);
897+
result = 31 * result + Objects.hashCode(additionalBeanStyleSetterName);
898+
881899
return result;
882900
}
883901
}

codegen/src/main/java/software/amazon/awssdk/codegen/model/service/Member.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ public class Member {
5959

6060
private String deprecatedName;
6161

62+
/**
63+
* An additional, alternate name for this member that should additionally be used when generating bean style
64+
* setters/getters for the enclosing shape.
65+
*/
66+
private String alternateBeanPropertyName;
67+
6268
private ContextParam contextParam;
6369

6470
public String getShape() {
@@ -236,4 +242,12 @@ public ContextParam getContextParam() {
236242
public void setContextParam(ContextParam contextParam) {
237243
this.contextParam = contextParam;
238244
}
245+
246+
public String getAlternateBeanPropertyName() {
247+
return alternateBeanPropertyName;
248+
}
249+
250+
public void setAlternateBeanPropertyName(String alternateBeanPropertyName) {
251+
this.alternateBeanPropertyName = alternateBeanPropertyName;
252+
}
239253
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/NonCollectionSetters.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ public List<MethodSpec> beanStyle() {
128128
.build());
129129
}
130130

131+
String additionalSetter = memberModel().getAdditionalBeanStyleSetterName();
132+
if (StringUtils.isNotBlank(additionalSetter)) {
133+
MethodSpec.Builder methodBuilder = beanStyleSetterBuilder();
134+
methodBuilder.setName(additionalSetter);
135+
methods.add(methodBuilder.addCode(beanCopySetterBody())
136+
.build());
137+
}
138+
131139
return methods;
132140
}
133141

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package software.amazon.awssdk.services.jsonprotocoltests.model;
2+
3+
import java.io.Serializable;
4+
import java.util.Arrays;
5+
import java.util.Collections;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Objects;
10+
import java.util.Optional;
11+
import java.util.function.BiConsumer;
12+
import java.util.function.Function;
13+
import software.amazon.awssdk.annotations.Generated;
14+
import software.amazon.awssdk.annotations.Mutable;
15+
import software.amazon.awssdk.annotations.NotThreadSafe;
16+
import software.amazon.awssdk.core.SdkField;
17+
import software.amazon.awssdk.core.SdkPojo;
18+
import software.amazon.awssdk.core.protocol.MarshallLocation;
19+
import software.amazon.awssdk.core.protocol.MarshallingType;
20+
import software.amazon.awssdk.core.traits.LocationTrait;
21+
import software.amazon.awssdk.utils.ToString;
22+
import software.amazon.awssdk.utils.builder.CopyableBuilder;
23+
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
24+
25+
/**
26+
*/
27+
@Generated("software.amazon.awssdk:codegen")
28+
public final class ContainsReservedKeyword implements SdkPojo, Serializable,
29+
ToCopyableBuilder<ContainsReservedKeyword.Builder, ContainsReservedKeyword> {
30+
private static final SdkField<String> NUL_FIELD = SdkField.<String> builder(MarshallingType.STRING).memberName("NUL")
31+
.getter(getter(ContainsReservedKeyword::nul)).setter(setter(Builder::nul))
32+
.traits(LocationTrait.builder().location(MarshallLocation.PAYLOAD).locationName("NULL").build()).build();
33+
34+
private static final List<SdkField<?>> SDK_FIELDS = Collections.unmodifiableList(Arrays.asList(NUL_FIELD));
35+
36+
private static final Map<String, SdkField<?>> SDK_NAME_TO_FIELD = memberNameToFieldInitializer();
37+
38+
private static final long serialVersionUID = 1L;
39+
40+
private final String nul;
41+
42+
private ContainsReservedKeyword(BuilderImpl builder) {
43+
this.nul = builder.nul;
44+
}
45+
46+
/**
47+
* Returns the value of the NUL property for this object.
48+
*
49+
* @return The value of the NUL property for this object.
50+
*/
51+
public final String nul() {
52+
return nul;
53+
}
54+
55+
@Override
56+
public Builder toBuilder() {
57+
return new BuilderImpl(this);
58+
}
59+
60+
public static Builder builder() {
61+
return new BuilderImpl();
62+
}
63+
64+
public static Class<? extends Builder> serializableBuilderClass() {
65+
return BuilderImpl.class;
66+
}
67+
68+
@Override
69+
public final int hashCode() {
70+
int hashCode = 1;
71+
hashCode = 31 * hashCode + Objects.hashCode(nul());
72+
return hashCode;
73+
}
74+
75+
@Override
76+
public final boolean equals(Object obj) {
77+
return equalsBySdkFields(obj);
78+
}
79+
80+
@Override
81+
public final boolean equalsBySdkFields(Object obj) {
82+
if (this == obj) {
83+
return true;
84+
}
85+
if (obj == null) {
86+
return false;
87+
}
88+
if (!(obj instanceof ContainsReservedKeyword)) {
89+
return false;
90+
}
91+
ContainsReservedKeyword other = (ContainsReservedKeyword) obj;
92+
return Objects.equals(nul(), other.nul());
93+
}
94+
95+
/**
96+
* Returns a string representation of this object. This is useful for testing and debugging. Sensitive data will be
97+
* redacted from this string using a placeholder value.
98+
*/
99+
@Override
100+
public final String toString() {
101+
return ToString.builder("ContainsReservedKeyword").add("NUL", nul()).build();
102+
}
103+
104+
public final <T> Optional<T> getValueForField(String fieldName, Class<T> clazz) {
105+
switch (fieldName) {
106+
case "NUL":
107+
return Optional.ofNullable(clazz.cast(nul()));
108+
default:
109+
return Optional.empty();
110+
}
111+
}
112+
113+
@Override
114+
public final List<SdkField<?>> sdkFields() {
115+
return SDK_FIELDS;
116+
}
117+
118+
@Override
119+
public final Map<String, SdkField<?>> sdkFieldNameToField() {
120+
return SDK_NAME_TO_FIELD;
121+
}
122+
123+
private static Map<String, SdkField<?>> memberNameToFieldInitializer() {
124+
Map<String, SdkField<?>> map = new HashMap<>();
125+
map.put("NULL", NUL_FIELD);
126+
return Collections.unmodifiableMap(map);
127+
}
128+
129+
private static <T> Function<Object, T> getter(Function<ContainsReservedKeyword, T> g) {
130+
return obj -> g.apply((ContainsReservedKeyword) obj);
131+
}
132+
133+
private static <T> BiConsumer<Object, T> setter(BiConsumer<Builder, T> s) {
134+
return (obj, val) -> s.accept((Builder) obj, val);
135+
}
136+
137+
@Mutable
138+
@NotThreadSafe
139+
public interface Builder extends SdkPojo, CopyableBuilder<Builder, ContainsReservedKeyword> {
140+
/**
141+
* Sets the value of the NUL property for this object.
142+
*
143+
* @param nul
144+
* The new value for the NUL property for this object.
145+
* @return Returns a reference to this object so that method calls can be chained together.
146+
*/
147+
Builder nul(String nul);
148+
}
149+
150+
static final class BuilderImpl implements Builder {
151+
private String nul;
152+
153+
private BuilderImpl() {
154+
}
155+
156+
private BuilderImpl(ContainsReservedKeyword model) {
157+
nul(model.nul);
158+
}
159+
160+
public final String getNul() {
161+
return nul;
162+
}
163+
164+
public final void setNul(String nul) {
165+
this.nul = nul;
166+
}
167+
168+
public final void setNull(String nul) {
169+
this.nul = nul;
170+
}
171+
172+
@Override
173+
public final Builder nul(String nul) {
174+
this.nul = nul;
175+
return this;
176+
}
177+
178+
@Override
179+
public ContainsReservedKeyword build() {
180+
return new ContainsReservedKeyword(this);
181+
}
182+
183+
@Override
184+
public List<SdkField<?>> sdkFields() {
185+
return SDK_FIELDS;
186+
}
187+
188+
@Override
189+
public Map<String, SdkField<?>> sdkFieldNameToField() {
190+
return SDK_NAME_TO_FIELD;
191+
}
192+
}
193+
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/customization.config

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66
"eventStreamOperation"
77
],
88
"shapeModifiers": {
9+
"ContainsReservedKeyword": {
10+
"modify": [
11+
{
12+
"NULL": {
13+
"emitPropertyName": "NUL",
14+
"alternateBeanPropertyName": "Null"
15+
}
16+
}
17+
]
18+
},
919
"DeprecatedRenameRequest": {
1020
"modify": [
1121
{

0 commit comments

Comments
 (0)