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.codegen.model.intermediate; 17 18 import static software.amazon.awssdk.codegen.internal.Constant.LF; 19 import static software.amazon.awssdk.codegen.internal.Constant.REQUEST_CLASS_SUFFIX; 20 import static software.amazon.awssdk.codegen.internal.Constant.RESPONSE_CLASS_SUFFIX; 21 import static software.amazon.awssdk.codegen.internal.DocumentationUtils.removeFromEnd; 22 23 import com.fasterxml.jackson.annotation.JsonIgnore; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.stream.Collectors; 30 import software.amazon.awssdk.codegen.model.intermediate.customization.ShapeCustomizationInfo; 31 import software.amazon.awssdk.codegen.model.service.XmlNamespace; 32 import software.amazon.awssdk.utils.StringUtils; 33 34 public class ShapeModel extends DocumentationModel implements HasDeprecation { 35 36 private String c2jName; 37 // shapeName might be later modified by the customization. 38 private String shapeName; 39 // the local variable name inside marshaller/unmarshaller implementation 40 private boolean deprecated; 41 private String deprecatedMessage; 42 private String type; 43 private List<String> required; 44 private boolean hasPayloadMember; 45 private boolean hasHeaderMember; 46 private boolean hasStatusCodeMember; 47 private boolean hasStreamingMember; 48 private boolean hasRequiresLengthMember; 49 private boolean wrapper; 50 private boolean simpleMethod; 51 private String requestSignerClassFqcn; 52 private EndpointDiscovery endpointDiscovery; 53 54 private List<MemberModel> members; 55 private List<EnumModel> enums; 56 57 private VariableModel variable; 58 59 private ShapeMarshaller marshaller; 60 private ShapeUnmarshaller unmarshaller; 61 62 private String errorCode; 63 private Integer httpStatusCode; 64 private boolean fault; 65 66 private ShapeCustomizationInfo customization = new ShapeCustomizationInfo(); 67 68 private boolean isEventStream; 69 70 private boolean isEvent; 71 72 private XmlNamespace xmlNamespace; 73 74 private boolean document; 75 76 private boolean union; 77 ShapeModel()78 public ShapeModel() { 79 } 80 ShapeModel(String c2jName)81 public ShapeModel(String c2jName) { 82 this.c2jName = c2jName; 83 } 84 getShapeName()85 public String getShapeName() { 86 return shapeName; 87 } 88 setShapeName(String shapeName)89 public void setShapeName(String shapeName) { 90 this.shapeName = shapeName; 91 } 92 93 @Override isDeprecated()94 public boolean isDeprecated() { 95 return deprecated; 96 } 97 setDeprecated(boolean deprecated)98 public void setDeprecated(boolean deprecated) { 99 this.deprecated = deprecated; 100 } 101 getDeprecatedMessage()102 public String getDeprecatedMessage() { 103 return deprecatedMessage; 104 } 105 setDeprecatedMessage(String deprecatedMessage)106 public void setDeprecatedMessage(String deprecatedMessage) { 107 this.deprecatedMessage = deprecatedMessage; 108 } 109 getC2jName()110 public String getC2jName() { 111 return c2jName; 112 } 113 setC2jName(String c2jName)114 public void setC2jName(String c2jName) { 115 this.c2jName = c2jName; 116 } 117 getType()118 public String getType() { 119 return type; 120 } 121 122 123 124 @JsonIgnore setType(ShapeType shapeType)125 public void setType(ShapeType shapeType) { 126 setType(shapeType.getValue()); 127 } 128 setType(String type)129 public void setType(String type) { 130 this.type = type; 131 } 132 133 @JsonIgnore getShapeType()134 public ShapeType getShapeType() { 135 return ShapeType.fromValue(type); 136 } 137 withType(String type)138 public ShapeModel withType(String type) { 139 this.type = type; 140 return this; 141 } 142 143 // Returns the list of C2j member names that are required for this shape. getRequired()144 public List<String> getRequired() { 145 return required; 146 } 147 setRequired(List<String> required)148 public void setRequired(List<String> required) { 149 this.required = required; 150 } 151 isHasPayloadMember()152 public boolean isHasPayloadMember() { 153 return hasPayloadMember; 154 } 155 setHasPayloadMember(boolean hasPayloadMember)156 public void setHasPayloadMember(boolean hasPayloadMember) { 157 this.hasPayloadMember = hasPayloadMember; 158 } 159 withHasPayloadMember(boolean hasPayloadMember)160 public ShapeModel withHasPayloadMember(boolean hasPayloadMember) { 161 setHasPayloadMember(hasPayloadMember); 162 return this; 163 } 164 165 /** 166 * @return The member explicitly designated as the payload member 167 */ 168 @JsonIgnore getPayloadMember()169 public MemberModel getPayloadMember() { 170 MemberModel payloadMember = null; 171 for (MemberModel member : members) { 172 if (member.getHttp().getIsPayload()) { 173 if (payloadMember == null) { 174 payloadMember = member; 175 } else { 176 throw new IllegalStateException( 177 String.format("Only one payload member can be explicitly set on %s. This is likely an error in " + 178 "the C2J model", c2jName)); 179 } 180 } 181 } 182 return payloadMember; 183 } 184 185 /** 186 * @return The list of members whose location is not specified. If no payload member is 187 * explicitly set then these members will appear in the payload 188 */ 189 @JsonIgnore getUnboundMembers()190 public List<MemberModel> getUnboundMembers() { 191 List<MemberModel> unboundMembers = new ArrayList<>(); 192 if (members != null) { 193 for (MemberModel member : members) { 194 if (member.getHttp().getLocation() == null && !member.getHttp().getIsPayload()) { 195 if (hasPayloadMember) { 196 // There is an explicit payload, but this unbound 197 // member isn't it. 198 // Note: Somewhat unintuitive, explicit payloads don't 199 // have an explicit location; they're identified by 200 // the payload HTTP trait being true. 201 throw new IllegalStateException(String.format( 202 "C2J Shape %s has both an explicit payload member and unbound (no explicit location) member, %s." 203 + " This is undefined behavior, verify the correctness of the C2J model.", 204 c2jName, member.getName())); 205 } 206 unboundMembers.add(member); 207 } 208 } 209 } 210 return unboundMembers; 211 } 212 213 /** 214 * @return The list of members whose are not marked with either eventheader or eventpayload trait. 215 */ 216 @JsonIgnore getUnboundEventMembers()217 public List<MemberModel> getUnboundEventMembers() { 218 if (members == null) { 219 return new ArrayList<>(); 220 } 221 222 return members.stream() 223 .filter(m -> !m.isEventHeader()) 224 .filter(m -> !m.isEventPayload()) 225 .collect(Collectors.toList()); 226 } 227 228 /** 229 * @return True if the shape has an explicit payload member or implicit payload member(s). 230 */ hasPayloadMembers()231 public boolean hasPayloadMembers() { 232 return hasPayloadMember || 233 getExplicitEventPayloadMember() != null || 234 hasImplicitPayloadMembers(); 235 236 } 237 hasImplicitPayloadMembers()238 public boolean hasImplicitPayloadMembers() { 239 return !getUnboundMembers().isEmpty() || 240 hasImplicitEventPayloadMembers(); 241 } 242 hasImplicitEventPayloadMembers()243 public boolean hasImplicitEventPayloadMembers() { 244 return isEvent() && !getUnboundEventMembers().isEmpty(); 245 } 246 247 /** 248 * Explicit event payload member will have "eventpayload" trait set to true. 249 * There can be at most only one member that can be declared as explicit payload. 250 * 251 * @return the member that has the 'eventpayload' trait set to true. If none found, return null. 252 */ getExplicitEventPayloadMember()253 public MemberModel getExplicitEventPayloadMember() { 254 if (members == null) { 255 return null; 256 } 257 258 return members.stream() 259 .filter(MemberModel::isEventPayload) 260 .findFirst() 261 .orElse(null); 262 } 263 264 /** 265 * If all members in shape have eventheader trait, then there is no payload 266 */ hasNoEventPayload()267 public boolean hasNoEventPayload() { 268 return members == null || members.stream().allMatch(MemberModel::isEventHeader); 269 } 270 isHasStreamingMember()271 public boolean isHasStreamingMember() { 272 return hasStreamingMember; 273 } 274 setHasStreamingMember(boolean hasStreamingMember)275 public void setHasStreamingMember(boolean hasStreamingMember) { 276 this.hasStreamingMember = hasStreamingMember; 277 } 278 withHasStreamingMember(boolean hasStreamingMember)279 public ShapeModel withHasStreamingMember(boolean hasStreamingMember) { 280 setHasStreamingMember(hasStreamingMember); 281 return this; 282 } 283 isHasRequiresLengthMember()284 public boolean isHasRequiresLengthMember() { 285 return hasRequiresLengthMember; 286 } 287 setHasRequiresLengthMember(boolean hasRequiresLengthMember)288 public void setHasRequiresLengthMember(boolean hasRequiresLengthMember) { 289 this.hasRequiresLengthMember = hasRequiresLengthMember; 290 } 291 withHasRequiresLengthMember(boolean hasRequiresLengthMember)292 public ShapeModel withHasRequiresLengthMember(boolean hasRequiresLengthMember) { 293 setHasRequiresLengthMember(hasRequiresLengthMember); 294 return this; 295 } 296 isHasHeaderMember()297 public boolean isHasHeaderMember() { 298 return hasHeaderMember; 299 } 300 setHasHeaderMember(boolean hasHeaderMember)301 public void setHasHeaderMember(boolean hasHeaderMember) { 302 this.hasHeaderMember = hasHeaderMember; 303 } 304 withHasHeaderMember(boolean hasHeaderMember)305 public ShapeModel withHasHeaderMember(boolean hasHeaderMember) { 306 setHasHeaderMember(hasHeaderMember); 307 return this; 308 } 309 isHasStatusCodeMember()310 public boolean isHasStatusCodeMember() { 311 return hasStatusCodeMember; 312 } 313 setHasStatusCodeMember(boolean hasStatusCodeMember)314 public void setHasStatusCodeMember(boolean hasStatusCodeMember) { 315 this.hasStatusCodeMember = hasStatusCodeMember; 316 } 317 isWrapper()318 public boolean isWrapper() { 319 return wrapper; 320 } 321 setWrapper(boolean wrapper)322 public void setWrapper(boolean wrapper) { 323 this.wrapper = wrapper; 324 } 325 isSimpleMethod()326 public boolean isSimpleMethod() { 327 return simpleMethod; 328 } 329 setSimpleMethod(boolean simpleMethod)330 public void setSimpleMethod(boolean simpleMethod) { 331 this.simpleMethod = simpleMethod; 332 } 333 withHasStatusCodeMember(boolean hasStatusCodeMember)334 public ShapeModel withHasStatusCodeMember(boolean hasStatusCodeMember) { 335 setHasStatusCodeMember(hasStatusCodeMember); 336 return this; 337 } 338 getMemberByVariableName(String memberVariableName)339 public MemberModel getMemberByVariableName(String memberVariableName) { 340 for (MemberModel memberModel : members) { 341 if (memberModel.getVariable().getVariableName().equals(memberVariableName)) { 342 return memberModel; 343 } 344 } 345 throw new IllegalArgumentException("Unknown member variable name: " + memberVariableName); 346 } 347 getMemberByName(String memberName)348 public MemberModel getMemberByName(String memberName) { 349 for (MemberModel memberModel : members) { 350 if (memberModel.getName().equals(memberName)) { 351 return memberModel; 352 } 353 } 354 return null; 355 } 356 getMemberByC2jName(String memberName)357 public MemberModel getMemberByC2jName(String memberName) { 358 for (MemberModel memberModel : members) { 359 if (memberModel.getC2jName().equals(memberName)) { 360 return memberModel; 361 } 362 } 363 return null; 364 } 365 getMembers()366 public List<MemberModel> getMembers() { 367 if (members == null) { 368 return Collections.emptyList(); 369 } 370 return members; 371 } 372 373 /** 374 * @return All non-streaming members of the shape. 375 */ getNonStreamingMembers()376 public List<MemberModel> getNonStreamingMembers() { 377 return getMembers().stream() 378 // Filter out binary streaming members 379 .filter(m -> !m.getHttp().getIsStreaming()) 380 // Filter out event stream members (if shape is null then it's primitive and we should include it). 381 .filter(m -> m.getShape() == null || !m.getShape().isEventStream) 382 .collect(Collectors.toList()); 383 } 384 setMembers(List<MemberModel> members)385 public void setMembers(List<MemberModel> members) { 386 this.members = members; 387 } 388 addMember(MemberModel member)389 public void addMember(MemberModel member) { 390 if (this.members == null) { 391 this.members = new ArrayList<>(); 392 } 393 members.add(member); 394 } 395 getEnums()396 public List<EnumModel> getEnums() { 397 return enums; 398 } 399 setEnums(List<EnumModel> enums)400 public void setEnums(List<EnumModel> enums) { 401 this.enums = enums; 402 } 403 addEnum(EnumModel enumModel)404 public void addEnum(EnumModel enumModel) { 405 if (this.enums == null) { 406 this.enums = new ArrayList<>(); 407 } 408 this.enums.add(enumModel); 409 } 410 getVariable()411 public VariableModel getVariable() { 412 return variable; 413 } 414 setVariable(VariableModel variable)415 public void setVariable(VariableModel variable) { 416 this.variable = variable; 417 } 418 getMarshaller()419 public ShapeMarshaller getMarshaller() { 420 return marshaller; 421 } 422 setMarshaller(ShapeMarshaller marshaller)423 public void setMarshaller(ShapeMarshaller marshaller) { 424 this.marshaller = marshaller; 425 } 426 getUnmarshaller()427 public ShapeUnmarshaller getUnmarshaller() { 428 return unmarshaller; 429 } 430 setUnmarshaller(ShapeUnmarshaller unmarshaller)431 public void setUnmarshaller(ShapeUnmarshaller unmarshaller) { 432 this.unmarshaller = unmarshaller; 433 } 434 getCustomization()435 public ShapeCustomizationInfo getCustomization() { 436 return customization; 437 } 438 setCustomization(ShapeCustomizationInfo customization)439 public void setCustomization(ShapeCustomizationInfo customization) { 440 this.customization = customization; 441 } 442 getMembersAsMap()443 public Map<String, MemberModel> getMembersAsMap() { 444 Map<String, MemberModel> shapeMembers = new HashMap<>(); 445 446 // Creating a map of shape's members. This map is used below when 447 // fetching the details of a member. 448 List<MemberModel> memberModels = getMembers(); 449 if (memberModels != null) { 450 for (MemberModel model : memberModels) { 451 shapeMembers.put(model.getName(), model); 452 } 453 } 454 return shapeMembers; 455 } 456 457 /** 458 * Tries to find the member model associated with the given c2j member name from this shape 459 * model. Returns the member model if present else returns null. 460 */ tryFindMemberModelByC2jName(String memberC2jName, boolean ignoreCase)461 public MemberModel tryFindMemberModelByC2jName(String memberC2jName, boolean ignoreCase) { 462 463 List<MemberModel> memberModels = getMembers(); 464 String expectedName = ignoreCase ? StringUtils.lowerCase(memberC2jName) 465 : memberC2jName; 466 467 if (memberModels != null) { 468 for (MemberModel member : memberModels) { 469 String actualName = ignoreCase ? StringUtils.lowerCase(member.getC2jName()) 470 : member.getC2jName(); 471 472 if (expectedName.equals(actualName)) { 473 return member; 474 } 475 } 476 } 477 return null; 478 } 479 480 /** 481 * Returns the member model associated with the given c2j member name from this shape model. 482 */ findMemberModelByC2jName(String memberC2jName)483 public MemberModel findMemberModelByC2jName(String memberC2jName) { 484 485 MemberModel model = tryFindMemberModelByC2jName(memberC2jName, false); 486 487 if (model == null) { 488 throw new IllegalArgumentException(memberC2jName + " member (c2j name) does not exist in the shape."); 489 } 490 491 return model; 492 } 493 494 /** 495 * Takes in the c2j member name as input and removes if the shape contains a member with the 496 * given name. Return false otherwise. 497 */ removeMemberByC2jName(String memberC2jName, boolean ignoreCase)498 public boolean removeMemberByC2jName(String memberC2jName, boolean ignoreCase) { 499 // Implicitly depending on the default equals and hashcode 500 // implementation of the class MemberModel 501 MemberModel model = tryFindMemberModelByC2jName(memberC2jName, ignoreCase); 502 return model == null ? false : members.remove(model); 503 } 504 505 /** 506 * Returns the enum model for the given enum value. 507 * Returns null if no such enum value exists. 508 */ findEnumModelByValue(String enumValue)509 public EnumModel findEnumModelByValue(String enumValue) { 510 511 if (enums != null) { 512 for (EnumModel enumModel : enums) { 513 if (enumValue.equals(enumModel.getValue())) { 514 return enumModel; 515 } 516 } 517 } 518 return null; 519 } 520 521 @JsonIgnore getDocumentationShapeName()522 public String getDocumentationShapeName() { 523 switch (getShapeType()) { 524 case Request: 525 return removeFromEnd(shapeName, REQUEST_CLASS_SUFFIX); 526 case Response: 527 return removeFromEnd(shapeName, RESPONSE_CLASS_SUFFIX); 528 default: 529 return c2jName; 530 } 531 } 532 getUnionTypeGetterDocumentation()533 public String getUnionTypeGetterDocumentation() { 534 return "Retrieve an enum value representing which member of this object is populated. " 535 + LF + LF 536 + "When this class is returned in a service response, this will be {@link Type#UNKNOWN_TO_SDK_VERSION} if the " 537 + "service returned a member that is only known to a newer SDK version." 538 + LF + LF 539 + "When this class is created directly in your code, this will be {@link Type#UNKNOWN_TO_SDK_VERSION} if zero " 540 + "members are set, and {@code null} if more than one member is set."; 541 } 542 543 @Override toString()544 public String toString() { 545 return shapeName; 546 } 547 getErrorCode()548 public String getErrorCode() { 549 return errorCode; 550 } 551 setErrorCode(String errorCode)552 public void setErrorCode(String errorCode) { 553 this.errorCode = errorCode; 554 } 555 556 /** 557 * Return the httpStatusCode of the exception shape. This value is present only for modeled exceptions. 558 */ getHttpStatusCode()559 public Integer getHttpStatusCode() { 560 return httpStatusCode; 561 } 562 setHttpStatusCode(Integer httpStatusCode)563 public void setHttpStatusCode(Integer httpStatusCode) { 564 this.httpStatusCode = httpStatusCode; 565 } 566 isRequestSignerAware()567 public boolean isRequestSignerAware() { 568 return requestSignerClassFqcn != null; 569 } 570 getRequestSignerClassFqcn()571 public String getRequestSignerClassFqcn() { 572 return requestSignerClassFqcn; 573 } 574 setRequestSignerClassFqcn(String authorizerClass)575 public void setRequestSignerClassFqcn(String authorizerClass) { 576 this.requestSignerClassFqcn = authorizerClass; 577 } 578 getEndpointDiscovery()579 public EndpointDiscovery getEndpointDiscovery() { 580 return endpointDiscovery; 581 } 582 setEndpointDiscovery(EndpointDiscovery endpointDiscovery)583 public void setEndpointDiscovery(EndpointDiscovery endpointDiscovery) { 584 this.endpointDiscovery = endpointDiscovery; 585 } 586 587 /** 588 * @return True if the shape is an 'eventstream' shape. The eventstream shape is the tagged union like 589 * container that holds individual 'events'. 590 */ isEventStream()591 public boolean isEventStream() { 592 return this.isEventStream; 593 } 594 withIsEventStream(boolean isEventStream)595 public ShapeModel withIsEventStream(boolean isEventStream) { 596 this.isEventStream = isEventStream; 597 return this; 598 } 599 600 /** 601 * @return True if the shape is an 'event'. I.E. It is a member of the eventstream and represents one logical event 602 * that can be delivered on the event stream. 603 */ isEvent()604 public boolean isEvent() { 605 return this.isEvent; 606 } 607 withIsEvent(boolean isEvent)608 public ShapeModel withIsEvent(boolean isEvent) { 609 this.isEvent = isEvent; 610 return this; 611 } 612 getXmlNamespace()613 public XmlNamespace getXmlNamespace() { 614 return xmlNamespace; 615 } 616 withXmlNamespace(XmlNamespace xmlNamespace)617 public ShapeModel withXmlNamespace(XmlNamespace xmlNamespace) { 618 this.xmlNamespace = xmlNamespace; 619 return this; 620 } 621 setXmlNamespace(XmlNamespace xmlNamespace)622 public void setXmlNamespace(XmlNamespace xmlNamespace) { 623 this.xmlNamespace = xmlNamespace; 624 } 625 isDocument()626 public boolean isDocument() { 627 return document; 628 } 629 withIsDocument(boolean document)630 public ShapeModel withIsDocument(boolean document) { 631 this.document = document; 632 return this; 633 } 634 isUnion()635 public boolean isUnion() { 636 return union; 637 } 638 withIsUnion(boolean union)639 public void withIsUnion(boolean union) { 640 this.union = union; 641 } 642 isFault()643 public boolean isFault() { 644 return fault; 645 } 646 withIsFault(boolean fault)647 public ShapeModel withIsFault(boolean fault) { 648 this.fault = fault; 649 return this; 650 } 651 } 652