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