15
15
16
16
package software .amazon .awssdk .enhanced .dynamodb ;
17
17
18
+ import java .util .Arrays ;
19
+ import java .util .Collection ;
18
20
import java .util .Collections ;
19
21
import java .util .HashMap ;
22
+ import java .util .LinkedList ;
20
23
import java .util .Map ;
24
+ import java .util .stream .Collectors ;
21
25
import software .amazon .awssdk .annotations .NotThreadSafe ;
22
26
import software .amazon .awssdk .annotations .SdkPublicApi ;
23
27
import software .amazon .awssdk .annotations .ThreadSafe ;
46
50
@ SdkPublicApi
47
51
@ ThreadSafe
48
52
public final class Expression {
53
+ public static final String AND = "AND" ;
54
+ public static final String OR = "OR" ;
55
+
49
56
private final String expression ;
50
57
private final Map <String , AttributeValue > expressionValues ;
51
58
private final Map <String , String > expressionNames ;
@@ -67,7 +74,7 @@ public static Builder builder() {
67
74
}
68
75
69
76
/**
70
- * Coalesces two complete expressions into a single expression. The expression string will be joined using the
77
+ * Coalesces two complete expressions into a single expression. The expression string will be joined using the
71
78
* supplied join token, and the ExpressionNames and ExpressionValues maps will be merged.
72
79
* @param expression1 The first expression to coalesce
73
80
* @param expression2 The second expression to coalesce
@@ -93,6 +100,61 @@ public static Expression join(Expression expression1, Expression expression2, St
93
100
.build ();
94
101
}
95
102
103
+ /**
104
+ * @see #join(String, Collection)
105
+ */
106
+ public static Expression and (Collection <Expression > expressions ) {
107
+ return join (AND , expressions );
108
+ }
109
+
110
+ /**
111
+ * @see #join(String, Collection)
112
+ */
113
+ public static Expression or (Collection <Expression > expressions ) {
114
+ return join (OR , expressions );
115
+ }
116
+
117
+ /**
118
+ * @see #join(String, Collection)
119
+ */
120
+ public static Expression join (String joinToken , Expression ... expressions ) {
121
+ return join (joinToken , Arrays .asList (expressions ));
122
+ }
123
+
124
+ /**
125
+ * Coalesces multiple complete expressions into a single expression. The expression string will be joined using the
126
+ * supplied join token, and the ExpressionNames and ExpressionValues maps will be merged.
127
+ * @param joinToken The join token to be used to join the expression strings (e.g.: 'AND', 'OR')
128
+ * @param expressions The expressions to coalesce
129
+ * @return The coalesced expression
130
+ * @throws IllegalArgumentException if a conflict occurs when merging ExpressionNames or ExpressionValues
131
+ */
132
+ public static Expression join (String joinToken , Collection <Expression > expressions ) {
133
+ joinToken = joinToken .trim ();
134
+ if (expressions .isEmpty ()) {
135
+ return null ;
136
+ }
137
+
138
+ if (expressions .size () == 1 ) {
139
+ return expressions .toArray (new Expression [] {})[0 ];
140
+ }
141
+
142
+ joinToken = ") " + joinToken + " (" ;
143
+ String expression = expressions .stream ()
144
+ .map (Expression ::expression )
145
+ .collect (Collectors .joining (joinToken , "(" , ")" ));
146
+
147
+ Builder builder = Expression .builder ()
148
+ .expression (expression );
149
+
150
+ expressions .forEach (expr -> {
151
+ builder .mergeExpressionValues (expr .expressionValues ())
152
+ .mergeExpressionNames (expr .expressionNames ());
153
+ });
154
+
155
+ return builder .build ();
156
+ }
157
+
96
158
/**
97
159
* Coalesces two expression strings into a single expression string. The expression string will be joined using the
98
160
* supplied join token.
@@ -198,6 +260,28 @@ public Expression and(Expression expression) {
198
260
return join (this , expression , " AND " );
199
261
}
200
262
263
+ /**
264
+ * Coalesces multiple complete expressions into a single expression joined by 'AND'.
265
+ *
266
+ * @see #join(String, Collection)
267
+ */
268
+ public Expression and (Expression ... expressions ) {
269
+ LinkedList <Expression > expressionList = new LinkedList <>(Arrays .asList (expressions ));
270
+ expressionList .addFirst (this );
271
+ return join (AND , expressionList );
272
+ }
273
+
274
+ /**
275
+ * Coalesces multiple complete expressions into a single expression joined by 'OR'.
276
+ *
277
+ * @see #join(String, Collection)
278
+ */
279
+ public Expression or (Expression ... expressions ) {
280
+ LinkedList <Expression > expressionList = new LinkedList <>(Arrays .asList (expressions ));
281
+ expressionList .addFirst (this );
282
+ return join (OR , expressionList );
283
+ }
284
+
201
285
@ Override
202
286
public boolean equals (Object o ) {
203
287
if (this == o ) {
@@ -255,6 +339,33 @@ public Builder expressionValues(Map<String, AttributeValue> expressionValues) {
255
339
return this ;
256
340
}
257
341
342
+ /**
343
+ * Merge the given ExpressionValues into the builders existing ExpressionValues
344
+ * @param expressionValues The values to merge into the ExpressionValues map
345
+ * @throws IllegalArgumentException if a conflict occurs when merging ExpressionValues
346
+ */
347
+ public Builder mergeExpressionValues (Map <String , AttributeValue > expressionValues ) {
348
+ if (this .expressionValues == null ) {
349
+ return expressionValues (expressionValues );
350
+ }
351
+
352
+ if (expressionValues == null ) {
353
+ return this ;
354
+ }
355
+
356
+ expressionValues .forEach ((key , value ) -> {
357
+ AttributeValue oldValue = this .expressionValues .put (key , value );
358
+
359
+ if (oldValue != null && !oldValue .equals (value )) {
360
+ throw new IllegalArgumentException (
361
+ String .format ("Attempt to coalesce expressions with conflicting expression values. "
362
+ + "Expression value key = '%s'" , key ));
363
+ }
364
+ });
365
+
366
+ return this ;
367
+ }
368
+
258
369
/**
259
370
* Adds a single element to the optional 'expression values' token map
260
371
*/
@@ -275,6 +386,33 @@ public Builder expressionNames(Map<String, String> expressionNames) {
275
386
return this ;
276
387
}
277
388
389
+ /**
390
+ * Merge the given ExpressionNames into the builders existing ExpressionNames
391
+ * @param expressionNames The values to merge into the ExpressionNames map
392
+ * @throws IllegalArgumentException if a conflict occurs when merging ExpressionNames
393
+ */
394
+ public Builder mergeExpressionNames (Map <String , String > expressionNames ) {
395
+ if (this .expressionNames == null ) {
396
+ return expressionNames (expressionNames );
397
+ }
398
+
399
+ if (expressionNames == null ) {
400
+ return this ;
401
+ }
402
+
403
+ expressionNames .forEach ((key , value ) -> {
404
+ String oldValue = this .expressionNames .put (key , value );
405
+
406
+ if (oldValue != null && !oldValue .equals (value )) {
407
+ throw new IllegalArgumentException (
408
+ String .format ("Attempt to coalesce expressions with conflicting expression names. "
409
+ + "Expression name key = '%s'" , key ));
410
+ }
411
+ });
412
+
413
+ return this ;
414
+ }
415
+
278
416
/**
279
417
* Adds a single element to the optional 'expression names' token map
280
418
*/
0 commit comments