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.enhanced.dynamodb.update; 17 18 import java.util.ArrayList; 19 import java.util.Arrays; 20 import java.util.Collections; 21 import java.util.List; 22 import java.util.stream.Collectors; 23 import java.util.stream.Stream; 24 import software.amazon.awssdk.annotations.SdkPublicApi; 25 26 /** 27 * Contains sets of {@link UpdateAction} that represent the four DynamoDB update actions: SET, ADD, REMOVE and DELETE. 28 * <p> 29 * Use this class to build an immutable UpdateExpression with one or more UpdateAction. An UpdateExpression may be merged 30 * with another. When two UpdateExpression are merged, the actions of each group of UpdateAction, should they exist, are 31 * combined; all SET actions from each expression are concatenated, all REMOVE actions etc. 32 * <p> 33 * DynamoDb Enhanced will convert the UpdateExpression to a format readable by DynamoDb, 34 * <p> 35 * Example:- 36 * <pre> 37 * {@code 38 * RemoveUpdateAction removeAction = ... 39 * SetUpdateAction setAction = ... 40 * UpdateExpression.builder() 41 * .addAction(removeAction) 42 * .addAction(setAction) 43 * .build(); 44 * } 45 * </pre> 46 * 47 * See respective subtype of {@link UpdateAction}, for example {@link SetAction}, for details on creating that action. 48 */ 49 @SdkPublicApi 50 public final class UpdateExpression { 51 52 private final List<RemoveAction> removeActions; 53 private final List<SetAction> setActions; 54 private final List<DeleteAction> deleteActions; 55 private final List<AddAction> addActions; 56 UpdateExpression(Builder builder)57 private UpdateExpression(Builder builder) { 58 this.removeActions = builder.removeActions; 59 this.setActions = builder.setActions; 60 this.deleteActions = builder.deleteActions; 61 this.addActions = builder.addActions; 62 } 63 64 /** 65 * Constructs a new builder for {@link UpdateExpression}. 66 * 67 * @return a new builder. 68 */ builder()69 public static Builder builder() { 70 return new Builder(); 71 } 72 removeActions()73 public List<RemoveAction> removeActions() { 74 return Collections.unmodifiableList(new ArrayList<>(removeActions)); 75 } 76 setActions()77 public List<SetAction> setActions() { 78 return Collections.unmodifiableList(new ArrayList<>(setActions)); 79 } 80 deleteActions()81 public List<DeleteAction> deleteActions() { 82 return Collections.unmodifiableList(new ArrayList<>(deleteActions)); 83 } 84 addActions()85 public List<AddAction> addActions() { 86 return Collections.unmodifiableList(new ArrayList<>(addActions)); 87 } 88 89 /** 90 * Merges two UpdateExpression, returning a new 91 */ mergeExpressions(UpdateExpression expression1, UpdateExpression expression2)92 public static UpdateExpression mergeExpressions(UpdateExpression expression1, UpdateExpression expression2) { 93 if (expression1 == null) { 94 return expression2; 95 } 96 if (expression2 == null) { 97 return expression1; 98 } 99 Builder builder = builder(); 100 builder.removeActions = Stream.concat(expression1.removeActions.stream(), 101 expression2.removeActions.stream()).collect(Collectors.toList()); 102 builder.setActions = Stream.concat(expression1.setActions.stream(), 103 expression2.setActions.stream()).collect(Collectors.toList()); 104 builder.deleteActions = Stream.concat(expression1.deleteActions.stream(), 105 expression2.deleteActions.stream()).collect(Collectors.toList()); 106 builder.addActions = Stream.concat(expression1.addActions.stream(), 107 expression2.addActions.stream()).collect(Collectors.toList()); 108 return builder.build(); 109 } 110 111 @Override equals(Object o)112 public boolean equals(Object o) { 113 if (this == o) { 114 return true; 115 } 116 if (o == null || getClass() != o.getClass()) { 117 return false; 118 } 119 120 UpdateExpression that = (UpdateExpression) o; 121 122 if (removeActions != null ? ! removeActions.equals(that.removeActions) : that.removeActions != null) { 123 return false; 124 } 125 if (setActions != null ? ! setActions.equals(that.setActions) : that.setActions != null) { 126 return false; 127 } 128 if (deleteActions != null ? ! deleteActions.equals(that.deleteActions) : that.deleteActions != null) { 129 return false; 130 } 131 return addActions != null ? addActions.equals(that.addActions) : that.addActions == null; 132 } 133 134 @Override hashCode()135 public int hashCode() { 136 int result = removeActions != null ? removeActions.hashCode() : 0; 137 result = 31 * result + (setActions != null ? setActions.hashCode() : 0); 138 result = 31 * result + (deleteActions != null ? deleteActions.hashCode() : 0); 139 result = 31 * result + (addActions != null ? addActions.hashCode() : 0); 140 return result; 141 } 142 143 /** 144 * A builder for {@link UpdateExpression} 145 */ 146 public static final class Builder { 147 148 private List<RemoveAction> removeActions = new ArrayList<>(); 149 private List<SetAction> setActions = new ArrayList<>(); 150 private List<DeleteAction> deleteActions = new ArrayList<>(); 151 private List<AddAction> addActions = new ArrayList<>(); 152 Builder()153 private Builder() { 154 } 155 156 /** 157 * Add an action of type {@link RemoveAction} 158 */ addAction(RemoveAction action)159 public Builder addAction(RemoveAction action) { 160 removeActions.add(action); 161 return this; 162 } 163 164 /** 165 * Add an action of type {@link SetAction} 166 */ addAction(SetAction action)167 public Builder addAction(SetAction action) { 168 setActions.add(action); 169 return this; 170 } 171 172 /** 173 * Add an action of type {@link DeleteAction} 174 */ addAction(DeleteAction action)175 public Builder addAction(DeleteAction action) { 176 deleteActions.add(action); 177 return this; 178 } 179 180 /** 181 * Add an action of type {@link AddAction} 182 */ addAction(AddAction action)183 public Builder addAction(AddAction action) { 184 addActions.add(action); 185 return this; 186 } 187 188 /** 189 * Adds a list of {@link UpdateAction} of any subtype to the builder, overwriting any previous values. 190 */ actions(List<? extends UpdateAction> actions)191 public Builder actions(List<? extends UpdateAction> actions) { 192 replaceActions(actions); 193 return this; 194 } 195 196 /** 197 * Adds a list of {@link UpdateAction} of any subtype to the builder, overwriting any previous values. 198 */ actions(UpdateAction... actions)199 public Builder actions(UpdateAction... actions) { 200 actions(Arrays.asList(actions)); 201 return this; 202 } 203 204 /** 205 * Builds an {@link UpdateExpression} based on the values stored in this builder. 206 */ build()207 public UpdateExpression build() { 208 return new UpdateExpression(this); 209 } 210 replaceActions(List<? extends UpdateAction> actions)211 private void replaceActions(List<? extends UpdateAction> actions) { 212 if (actions != null) { 213 this.removeActions = new ArrayList<>(); 214 this.setActions = new ArrayList<>(); 215 this.deleteActions = new ArrayList<>(); 216 this.addActions = new ArrayList<>(); 217 actions.forEach(this::assignAction); 218 } 219 } 220 assignAction(UpdateAction action)221 private void assignAction(UpdateAction action) { 222 if (action instanceof RemoveAction) { 223 addAction((RemoveAction) action); 224 } else if (action instanceof SetAction) { 225 addAction((SetAction) action); 226 } else if (action instanceof DeleteAction) { 227 addAction((DeleteAction) action); 228 } else if (action instanceof AddAction) { 229 addAction((AddAction) action); 230 } else { 231 throw new IllegalArgumentException( 232 String.format("Do not recognize UpdateAction: %s", action.getClass())); 233 } 234 } 235 } 236 } 237