1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
5  * the License. A copy of the License is located at
6  *
7  * http://aws.amazon.com/apache2.0
8  *
9  * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
10  * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
11  * and limitations under the License.
12  */
13 
14 package software.amazon.awssdk.services.kms.endpoints.internal;
15 
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.function.BiConsumer;
21 import java.util.stream.Collectors;
22 import software.amazon.awssdk.annotations.SdkInternalApi;
23 import software.amazon.awssdk.core.exception.SdkClientException;
24 import software.amazon.awssdk.protocols.jsoncore.JsonNode;
25 
26 /**
27  * Base class for the types of values computable by the {@link RuleEngine}.
28  */
29 @SdkInternalApi
30 public abstract class Value {
31 
isNone()32     public boolean isNone() {
33         return false;
34     }
35 
expectString()36     public String expectString() {
37         throw new RuntimeException("Expected string but was: " + this);
38     }
39 
expectBool()40     public boolean expectBool() {
41         throw new RuntimeException("Expected bool but was: " + this);
42     }
43 
expectRecord()44     public Record expectRecord() {
45         throw new RuntimeException("Expected object but was: " + this);
46     }
47 
expectEndpoint()48     public Endpoint expectEndpoint() {
49         throw new RuntimeException("Expected endpoint, found " + this);
50     }
51 
expectArray()52     public Array expectArray() {
53         throw new RuntimeException("Expected array, found " + this);
54     }
55 
expectInt()56     public int expectInt() {
57         throw new RuntimeException("Expected int, found " + this);
58     }
59 
fromNode(JsonNode node)60     public static Value fromNode(JsonNode node) {
61         if (node.isArray()) {
62             return new Array(node.asArray().stream().map(Value::fromNode).collect(Collectors.toList()));
63         } else if (node.isBoolean()) {
64             return fromBool(node.asBoolean());
65         } else if (node.isNull()) {
66             throw SdkClientException.create("null cannot be used as a literal");
67         } else if (node.isNumber()) {
68             return fromInteger(Integer.parseInt(node.asNumber()));
69         } else if (node.isObject()) {
70             HashMap<Identifier, Value> out = new HashMap<>();
71             node.asObject().forEach((k, v) -> out.put(Identifier.of(k), fromNode(v)));
72             return fromRecord(out);
73         } else if (node.isString()) {
74             return fromStr(node.asString());
75         }
76         throw SdkClientException.create("Unable to create Value from " + node);
77     }
78 
endpointFromNode(JsonNode source)79     public static Endpoint endpointFromNode(JsonNode source) {
80         return Endpoint.fromNode(source);
81     }
82 
83     /**
84      * A string value.
85      */
86     public static class Str extends Value {
87         private final String value;
88 
Str(String value)89         private Str(String value) {
90             this.value = value;
91         }
92 
93         @Override
expectString()94         public String expectString() {
95             return value;
96         }
97 
98         @Override
toString()99         public String toString() {
100             return "Str{" + "value='" + value + '\'' + '}';
101         }
102 
103         @Override
equals(Object o)104         public boolean equals(Object o) {
105             if (this == o) {
106                 return true;
107             }
108             if (o == null || getClass() != o.getClass()) {
109                 return false;
110             }
111 
112             Str str = (Str) o;
113 
114             return value != null ? value.equals(str.value) : str.value == null;
115         }
116 
117         @Override
hashCode()118         public int hashCode() {
119             return value != null ? value.hashCode() : 0;
120         }
121     }
122 
123     /**
124      * An integer value.
125      */
126     public static class Int extends Value {
127         private final int value;
128 
Int(int value)129         private Int(int value) {
130             this.value = value;
131         }
132 
133         @Override
expectInt()134         public int expectInt() {
135             return value;
136         }
137 
138         @Override
equals(Object o)139         public boolean equals(Object o) {
140             if (this == o) {
141                 return true;
142             }
143             if (o == null || getClass() != o.getClass()) {
144                 return false;
145             }
146 
147             Int anInt = (Int) o;
148 
149             return value == anInt.value;
150         }
151 
152         @Override
hashCode()153         public int hashCode() {
154             return value;
155         }
156     }
157 
158     /**
159      * A boolean value.
160      */
161     public static class Bool extends Value {
162         private final boolean value;
163 
Bool(boolean value)164         private Bool(boolean value) {
165             this.value = value;
166         }
167 
168         @Override
expectBool()169         public boolean expectBool() {
170             return value;
171         }
172 
173         @Override
equals(Object o)174         public boolean equals(Object o) {
175             if (this == o) {
176                 return true;
177             }
178             if (o == null || getClass() != o.getClass()) {
179                 return false;
180             }
181 
182             Bool bool = (Bool) o;
183 
184             return value == bool.value;
185         }
186 
187         @Override
hashCode()188         public int hashCode() {
189             return value ? 1 : 0;
190         }
191     }
192 
193     /**
194      * An array value.
195      */
196     public static class Array extends Value {
197         private List<Value> inner;
198 
Array(List<Value> inner)199         private Array(List<Value> inner) {
200             this.inner = inner;
201         }
202 
203         @Override
expectArray()204         public Array expectArray() {
205             return this;
206         }
207 
get(int idx)208         public Value get(int idx) {
209             if (this.inner.size() > idx) {
210                 return this.inner.get(idx);
211             } else {
212                 return new Value.None();
213             }
214         }
215 
size()216         public int size() {
217             return inner.size();
218         }
219 
220         @Override
toString()221         public String toString() {
222             return "Array{" + "inner=" + inner + '}';
223         }
224 
225         @Override
equals(Object o)226         public boolean equals(Object o) {
227             if (this == o) {
228                 return true;
229             }
230             if (o == null || getClass() != o.getClass()) {
231                 return false;
232             }
233 
234             Array array = (Array) o;
235 
236             return inner != null ? inner.equals(array.inner) : array.inner == null;
237         }
238 
239         @Override
hashCode()240         public int hashCode() {
241             return inner != null ? inner.hashCode() : 0;
242         }
243     }
244 
245     /**
246      * A record (map) value.
247      */
248     public static class Record extends Value {
249         private final Map<Identifier, Value> value;
250 
Record(Map<Identifier, Value> value)251         private Record(Map<Identifier, Value> value) {
252             this.value = value;
253         }
254 
get(Identifier id)255         public Value get(Identifier id) {
256             return value.get(id);
257         }
258 
getValue()259         public Map<Identifier, Value> getValue() {
260             return value;
261         }
262 
forEach(BiConsumer<Identifier, Value> fn)263         public void forEach(BiConsumer<Identifier, Value> fn) {
264             value.forEach(fn);
265         }
266 
267         @Override
expectRecord()268         public Record expectRecord() {
269             return this;
270         }
271 
272         @Override
toString()273         public String toString() {
274             return "Record{" + "value=" + value + '}';
275         }
276 
277         @Override
equals(Object o)278         public boolean equals(Object o) {
279             if (this == o) {
280                 return true;
281             }
282             if (o == null || getClass() != o.getClass()) {
283                 return false;
284             }
285 
286             Record record = (Record) o;
287 
288             return value != null ? value.equals(record.value) : record.value == null;
289         }
290 
291         @Override
hashCode()292         public int hashCode() {
293             return value != null ? value.hashCode() : 0;
294         }
295     }
296 
297     public static class Endpoint extends Value {
298         private static final String URL = "url";
299         private static final String PROPERTIES = "properties";
300         private static final String HEADERS = "headers";
301 
302         private final String url;
303         private final Map<String, Value> properties;
304         private final Map<String, List<String>> headers;
305 
Endpoint(Builder b)306         private Endpoint(Builder b) {
307             this.url = b.url;
308             this.properties = b.properties;
309             this.headers = b.headers;
310         }
311 
getUrl()312         public String getUrl() {
313             return url;
314         }
315 
getProperties()316         public Map<String, Value> getProperties() {
317             return properties;
318         }
319 
getHeaders()320         public Map<String, List<String>> getHeaders() {
321             return headers;
322         }
323 
324         @Override
expectEndpoint()325         public Endpoint expectEndpoint() {
326             return this;
327         }
328 
329         @Override
equals(Object o)330         public boolean equals(Object o) {
331             if (this == o) {
332                 return true;
333             }
334             if (o == null || getClass() != o.getClass()) {
335                 return false;
336             }
337 
338             Endpoint endpoint = (Endpoint) o;
339 
340             if (url != null ? !url.equals(endpoint.url) : endpoint.url != null) {
341                 return false;
342             }
343             if (properties != null ? !properties.equals(endpoint.properties) : endpoint.properties != null) {
344                 return false;
345             }
346             return headers != null ? headers.equals(endpoint.headers) : endpoint.headers == null;
347         }
348 
349         @Override
hashCode()350         public int hashCode() {
351             int result = url != null ? url.hashCode() : 0;
352             result = 31 * result + (properties != null ? properties.hashCode() : 0);
353             result = 31 * result + (headers != null ? headers.hashCode() : 0);
354             return result;
355         }
356 
357         @Override
toString()358         public String toString() {
359             return "Endpoint{" + "url='" + url + '\'' + ", properties=" + properties + ", headers=" + headers + '}';
360         }
361 
fromNode(JsonNode node)362         public static Endpoint fromNode(JsonNode node) {
363             Builder b = builder();
364 
365             Map<String, JsonNode> objNode = node.asObject();
366 
367             b.url(objNode.get(URL).asString());
368 
369             JsonNode propertiesNode = objNode.get(PROPERTIES);
370             if (propertiesNode != null) {
371                 propertiesNode.asObject().forEach((k, v) -> {
372                     b.property(k, Value.fromNode(v));
373                 });
374             }
375 
376             JsonNode headersNode = objNode.get(HEADERS);
377             if (headersNode != null) {
378                 headersNode.asObject().forEach((k, v) -> v.asArray().forEach(e -> b.addHeader(k, e.asString())));
379             }
380 
381             return b.build();
382         }
383 
builder()384         public static Builder builder() {
385             return new Builder();
386         }
387 
388         public static class Builder {
389             private String url;
390             private final Map<String, Value> properties = new HashMap<>();
391             private final Map<String, List<String>> headers = new HashMap<>();
392 
url(String url)393             public Builder url(String url) {
394                 this.url = url;
395                 return this;
396             }
397 
properties(Map<String, Value> properties)398             public Builder properties(Map<String, Value> properties) {
399                 this.properties.clear();
400                 this.properties.putAll(properties);
401                 return this;
402             }
403 
property(String name, Value value)404             public Builder property(String name, Value value) {
405                 this.properties.put(name, value);
406                 return this;
407             }
408 
addHeader(String name, String value)409             public Builder addHeader(String name, String value) {
410                 List<String> values = this.headers.computeIfAbsent(name, (k) -> new ArrayList<>());
411                 values.add(value);
412                 return this;
413             }
414 
build()415             public Endpoint build() {
416                 return new Endpoint(this);
417             }
418         }
419     }
420 
421     public static class None extends Value {
422         @Override
isNone()423         public boolean isNone() {
424             return true;
425         }
426     }
427 
fromStr(String value)428     public static Str fromStr(String value) {
429         return new Str(value);
430     }
431 
fromInteger(int value)432     public static Int fromInteger(int value) {
433         return new Int(value);
434     }
435 
fromBool(boolean value)436     public static Bool fromBool(boolean value) {
437         return new Bool(value);
438     }
439 
fromArray(List<Value> value)440     public static Array fromArray(List<Value> value) {
441         return new Array(value);
442     }
443 
fromRecord(Map<Identifier, Value> value)444     public static Record fromRecord(Map<Identifier, Value> value) {
445         return new Record(value);
446     }
447 
none()448     public static None none() {
449         return new None();
450     }
451 }
452