• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.core;
17 
18 import java.util.Arrays;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Optional;
22 import java.util.function.BiConsumer;
23 import java.util.function.Function;
24 import java.util.function.Supplier;
25 import software.amazon.awssdk.annotations.SdkProtectedApi;
26 import software.amazon.awssdk.core.protocol.MarshallLocation;
27 import software.amazon.awssdk.core.protocol.MarshallingType;
28 import software.amazon.awssdk.core.traits.DefaultValueTrait;
29 import software.amazon.awssdk.core.traits.LocationTrait;
30 import software.amazon.awssdk.core.traits.Trait;
31 
32 /**
33  * Metadata about a member in an {@link SdkPojo}. Contains information about how to marshall/unmarshall.
34  *
35  * @param <TypeT> Java Type of member.
36  */
37 @SdkProtectedApi
38 public final class SdkField<TypeT> {
39     private final String memberName;
40     private final MarshallingType<? super TypeT> marshallingType;
41     private final MarshallLocation location;
42     private final String locationName;
43     private final String unmarshallLocationName;
44     private final Supplier<SdkPojo> constructor;
45     private final BiConsumer<Object, TypeT> setter;
46     private final Function<Object, TypeT> getter;
47     private final Map<Class<? extends Trait>, Trait> traits;
48 
SdkField(Builder<TypeT> builder)49     private SdkField(Builder<TypeT> builder) {
50         this.memberName = builder.memberName;
51         this.marshallingType = builder.marshallingType;
52         this.traits = new HashMap<>(builder.traits);
53         this.constructor = builder.constructor;
54         this.setter = builder.setter;
55         this.getter = builder.getter;
56 
57         // Eagerly dereference location trait since it's so commonly used.
58         LocationTrait locationTrait = getTrait(LocationTrait.class);
59         this.location = locationTrait.location();
60         this.locationName = locationTrait.locationName();
61         this.unmarshallLocationName = locationTrait.unmarshallLocationName();
62     }
63 
memberName()64     public String memberName() {
65         return memberName;
66     }
67 
68     /**
69      * @return MarshallingType of member. Used primarily for marshaller/unmarshaller lookups.
70      */
marshallingType()71     public MarshallingType<? super TypeT> marshallingType() {
72         return marshallingType;
73     }
74 
75     /**
76      * @return Location the member should be marshalled into (i.e. headers/query/path/payload).
77      */
location()78     public MarshallLocation location() {
79         return location;
80     }
81 
82     /**
83      * @return The location name to use when marshalling. I.E. the field name of the JSON document, or the header name, etc.
84      */
locationName()85     public String locationName() {
86         return locationName;
87     }
88 
89     /**
90      * @return The location name to use when unmarshalling. This is only needed for AWS/Query or EC2 services. All
91      * other services should use {@link #locationName} for both marshalling and unmarshalling.
92      */
unmarshallLocationName()93     public String unmarshallLocationName() {
94         return unmarshallLocationName;
95     }
96 
constructor()97     public Supplier<SdkPojo> constructor() {
98         return constructor;
99     }
100 
101     /**
102      * Gets the trait of the specified class if available.
103      *
104      * @param clzz Trait class to get.
105      * @param <T> Type of trait.
106      * @return Trait instance or null if trait is not present.
107      */
108     @SuppressWarnings("unchecked")
getTrait(Class<T> clzz)109     public <T extends Trait> T getTrait(Class<T> clzz) {
110         return (T) traits.get(clzz);
111     }
112 
113     /**
114      * Gets the trait of the specified class if available.
115      *
116      * @param clzz Trait class to get.
117      * @param <T> Type of trait.
118      * @return Optional of trait instance.
119      */
120     @SuppressWarnings("unchecked")
getOptionalTrait(Class<T> clzz)121     public <T extends Trait> Optional<T> getOptionalTrait(Class<T> clzz) {
122         return Optional.ofNullable((T) traits.get(clzz));
123     }
124 
125     /**
126      * Gets the trait of the specified class, or throw {@link IllegalStateException} if not available.
127      *
128      * @param clzz Trait class to get.
129      * @param <T> Type of trait.
130      * @return Trait instance.
131      * @throws IllegalStateException if trait is not present.
132      */
133     @SuppressWarnings("unchecked")
getRequiredTrait(Class<T> clzz)134     public <T extends Trait> T getRequiredTrait(Class<T> clzz) throws IllegalStateException {
135         T trait = (T) traits.get(clzz);
136         if (trait == null) {
137             throw new IllegalStateException(memberName + " member is missing " + clzz.getSimpleName());
138         }
139         return trait;
140     }
141 
142     /**
143      * Checks if a given {@link Trait} is present on the field.
144      *
145      * @param clzz Trait class to check.
146      * @return True if trait is present, false if not.
147      */
containsTrait(Class<? extends Trait> clzz)148     public boolean containsTrait(Class<? extends Trait> clzz) {
149         return traits.containsKey(clzz);
150     }
151 
152     /**
153      * Retrieves the current value of 'this' field from the given POJO. Uses the getter passed into the {@link Builder}.
154      *
155      * @param pojo POJO to retrieve value from.
156      * @return Current value of 'this' field in the POJO.
157      */
get(Object pojo)158     private TypeT get(Object pojo) {
159         return getter.apply(pojo);
160     }
161 
162     /**
163      * Retrieves the current value of 'this' field from the given POJO. Uses the getter passed into the {@link Builder}. If the
164      * current value is null this method will look for the {@link DefaultValueTrait} on the field and attempt to resolve a default
165      * value. If the {@link DefaultValueTrait} is not present this just returns null.
166      *
167      * @param pojo POJO to retrieve value from.
168      * @return Current value of 'this' field in the POJO or default value if current value is null.
169      */
getValueOrDefault(Object pojo)170     public TypeT getValueOrDefault(Object pojo) {
171         TypeT val = this.get(pojo);
172         DefaultValueTrait trait = getTrait(DefaultValueTrait.class);
173         return (trait == null ? val : (TypeT) trait.resolveValue(val));
174     }
175 
176     /**
177      * Sets the given value on the POJO via the setter passed into the {@link Builder}.
178      *
179      * @param pojo POJO containing field to set.
180      * @param val Value of field.
181      */
182     @SuppressWarnings("unchecked")
set(Object pojo, Object val)183     public void set(Object pojo, Object val) {
184         setter.accept(pojo, (TypeT) val);
185     }
186 
187     /**
188      * Creates a new instance of {@link Builder} bound to the specified type.
189      *
190      * @param marshallingType Type of field.
191      * @param <TypeT> Type of field. Must be a subtype of the {@link MarshallingType} type param.
192      * @return New builder instance.
193      */
builder(MarshallingType<? super TypeT> marshallingType)194     public static <TypeT> Builder<TypeT> builder(MarshallingType<? super TypeT> marshallingType) {
195         return new Builder<>(marshallingType);
196     }
197 
198     /**
199      * Builder for {@link SdkField}.
200      *
201      * @param <TypeT> Java type of field.
202      */
203     public static final class Builder<TypeT> {
204 
205         private final MarshallingType<? super TypeT> marshallingType;
206         private String memberName;
207         private Supplier<SdkPojo> constructor;
208         private BiConsumer<Object, TypeT> setter;
209         private Function<Object, TypeT> getter;
210         private final Map<Class<? extends Trait>, Trait> traits = new HashMap<>();
211 
Builder(MarshallingType<? super TypeT> marshallingType)212         private Builder(MarshallingType<? super TypeT> marshallingType) {
213             this.marshallingType = marshallingType;
214         }
215 
memberName(String memberName)216         public Builder<TypeT> memberName(String memberName) {
217             this.memberName = memberName;
218             return this;
219         }
220 
221         /**
222          * Sets a {@link Supplier} which will create a new <b>MUTABLE</b> instance of the POJO. I.E. this will
223          * create the Builder for a given POJO and not the immutable POJO itself.
224          *
225          * @param constructor Supplier method to create the mutable POJO.
226          * @return This object for method chaining.
227          */
constructor(Supplier<SdkPojo> constructor)228         public Builder<TypeT> constructor(Supplier<SdkPojo> constructor) {
229             this.constructor = constructor;
230             return this;
231         }
232 
233         /**
234          * Sets the {@link BiConsumer} which will accept an object and a value and set that value on the appropriate
235          * member of the object. This requires a <b>MUTABLE</b> pojo so thus this setter will be on the Builder
236          * for the given POJO.
237          *
238          * @param setter Setter method.
239          * @return This object for method chaining.
240          */
setter(BiConsumer<Object, TypeT> setter)241         public Builder<TypeT> setter(BiConsumer<Object, TypeT> setter) {
242             this.setter = setter;
243             return this;
244         }
245 
246         /**
247          * Sets the {@link Function} that will accept an object and return the current value of 'this' field on that object.
248          * This will typically be a getter on the immutable representation of the POJO and is used mostly during marshalling.
249          *
250          * @param getter Getter method.
251          * @return This object for method chaining.
252          */
getter(Function<Object, TypeT> getter)253         public Builder<TypeT> getter(Function<Object, TypeT> getter) {
254             this.getter = getter;
255             return this;
256         }
257 
258         /**
259          * Attaches one or more traits to the {@link SdkField}. Traits can have additional metadata and behavior that
260          * influence how a field is marshalled/unmarshalled.
261          *
262          * @param traits Traits to attach.
263          * @return This object for method chaining.
264          */
traits(Trait... traits)265         public Builder<TypeT> traits(Trait... traits) {
266             Arrays.stream(traits).forEach(t -> this.traits.put(t.getClass(), t));
267             return this;
268         }
269 
270         /**
271          * @return An immutable {@link SdkField}.
272          */
build()273         public SdkField<TypeT> build() {
274             return new SdkField<>(this);
275         }
276     }
277 }
278