|
93 | 93 | public class ProjectionExpression { |
94 | 94 |
|
95 | 95 | private static final String AMZN_MAPPED = "#AMZN_MAPPED_"; |
96 | | - private static final UnaryOperator<String> PROJECTION_EXPRESSION_KEY_MAPPER = k -> AMZN_MAPPED + cleanAttributeName(k); |
| 96 | + private static final UnaryOperator<String> PROJECTION_EXPRESSION_KEY_MAPPER = |
| 97 | + k -> AMZN_MAPPED + cleanAttributeName(attributeNameWithoutListDereference(k)); |
97 | 98 |
|
98 | 99 | private final Optional<String> projectionExpressionAsString; |
99 | 100 | private final Map<String, String> expressionAttributeNames; |
@@ -132,6 +133,7 @@ private static Map<String, String> createAttributePlaceholders(List<NestedAttrib |
132 | 133 | Map<String, List<String>> placeholderToAttributeNames = |
133 | 134 | nestedAttributeNames.stream() |
134 | 135 | .flatMap(n -> n.elements().stream()) |
| 136 | + .map(ProjectionExpression::attributeNameWithoutListDereference) |
135 | 137 | .distinct() |
136 | 138 | .collect(Collectors.groupingBy(PROJECTION_EXPRESSION_KEY_MAPPER, Collectors.toList())); |
137 | 139 |
|
@@ -180,8 +182,45 @@ private static String convertToNameExpression(NestedAttributeName nestedAttribut |
180 | 182 | Map<String, String> attributeToSanitizedMap) { |
181 | 183 | return nestedAttributeName.elements() |
182 | 184 | .stream() |
183 | | - .map(attributeToSanitizedMap::get) |
| 185 | + .map(element -> attributeToSanitizedMap.get(attributeNameWithoutListDereference(element)) |
| 186 | + + listDereferenceSuffix(element)) |
184 | 187 | .collect(Collectors.joining(".")); |
185 | 188 | } |
186 | 189 |
|
| 190 | + private static String attributeNameWithoutListDereference(String attributeName) { |
| 191 | + int firstListDereferenceIndex = firstListDereferenceIndex(attributeName); |
| 192 | + return firstListDereferenceIndex == -1 ? attributeName : attributeName.substring(0, firstListDereferenceIndex); |
| 193 | + } |
| 194 | + |
| 195 | + private static String listDereferenceSuffix(String attributeName) { |
| 196 | + int firstListDereferenceIndex = firstListDereferenceIndex(attributeName); |
| 197 | + return firstListDereferenceIndex == -1 ? "" : attributeName.substring(firstListDereferenceIndex); |
| 198 | + } |
| 199 | + |
| 200 | + private static int firstListDereferenceIndex(String attributeName) { |
| 201 | + int position = attributeName.length(); |
| 202 | + int firstListDereferenceIndex = position; |
| 203 | + |
| 204 | + while (position > 0 && attributeName.charAt(position - 1) == ']') { |
| 205 | + int openBracketIndex = attributeName.lastIndexOf('[', position - 1); |
| 206 | + if (openBracketIndex == -1 || openBracketIndex == position - 1 || |
| 207 | + !containsOnlyDigits(attributeName, openBracketIndex + 1, position - 1)) { |
| 208 | + return -1; |
| 209 | + } |
| 210 | + firstListDereferenceIndex = openBracketIndex; |
| 211 | + position = openBracketIndex; |
| 212 | + } |
| 213 | + |
| 214 | + return firstListDereferenceIndex == attributeName.length() ? -1 : firstListDereferenceIndex; |
| 215 | + } |
| 216 | + |
| 217 | + private static boolean containsOnlyDigits(String value, int beginIndex, int endIndex) { |
| 218 | + for (int i = beginIndex; i < endIndex; i++) { |
| 219 | + if (!Character.isDigit(value.charAt(i))) { |
| 220 | + return false; |
| 221 | + } |
| 222 | + } |
| 223 | + return true; |
| 224 | + } |
| 225 | + |
187 | 226 | } |
0 commit comments