Skip to content

Commit d816d61

Browse files
committed
Retain both the original deferred words and their bases, and expose them
with separate methods
1 parent 602a730 commit d816d61

File tree

5 files changed

+84
-57
lines changed

5 files changed

+84
-57
lines changed

src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,8 @@ private boolean alreadyDeferredInEarlierCall(
237237
Objects.equals(importResourcePath, deferredToken.getImportResourcePath())
238238
)
239239
.anyMatch(deferredToken ->
240-
deferredToken.getSetDeferredWords().contains(key) ||
241-
deferredToken
242-
.getUsedDeferredWords()
243-
.stream()
244-
.anyMatch(used -> key.equals(used.split("\\.", 2)[0]))
240+
deferredToken.getSetDeferredBases().contains(key) ||
241+
deferredToken.getUsedDeferredBases().contains(key)
245242
);
246243
}
247244
return false;

src/main/java/com/hubspot/jinjava/lib/tag/eager/DeferredToken.java

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.google.common.annotations.Beta;
44
import com.google.common.base.Strings;
5+
import com.google.common.collect.ImmutableSet;
56
import com.hubspot.jinjava.interpret.CallStack;
67
import com.hubspot.jinjava.interpret.Context;
78
import com.hubspot.jinjava.interpret.DeferredLazyReference;
@@ -31,34 +32,18 @@ public class DeferredToken {
3132
public static class DeferredTokenBuilder {
3233

3334
private final Token token;
34-
private Stream<String> usedDeferredWords;
35-
private Stream<String> setDeferredWords;
35+
private ImmutableSet.Builder<String> usedDeferredWords;
36+
private ImmutableSet.Builder<String> setDeferredWords;
3637

3738
private DeferredTokenBuilder(Token token) {
3839
this.token = token;
3940
}
4041

4142
public DeferredToken build() {
42-
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();
4343
return new DeferredToken(
4444
token,
45-
usedDeferredWords != null
46-
? usedDeferredWords
47-
.map(DeferredToken::splitToken)
48-
.map(DeferredToken::getFirstNonEmptyToken)
49-
.distinct()
50-
.filter(word ->
51-
interpreter == null ||
52-
!(interpreter.getContext().get(word) instanceof DeferredMacroValueImpl)
53-
)
54-
.collect(Collectors.toSet())
55-
: Collections.emptySet(),
56-
setDeferredWords != null
57-
? setDeferredWords
58-
.map(DeferredToken::splitToken)
59-
.map(DeferredToken::getFirstNonEmptyToken)
60-
.collect(Collectors.toSet())
61-
: Collections.emptySet(),
45+
usedDeferredWords != null ? usedDeferredWords.build() : Collections.emptySet(),
46+
setDeferredWords != null ? setDeferredWords.build() : Collections.emptySet(),
6247
acquireImportResourcePath(),
6348
acquireMacroStack()
6449
);
@@ -67,34 +52,40 @@ public DeferredToken build() {
6752
public DeferredTokenBuilder addUsedDeferredWords(
6853
Collection<String> usedDeferredWordsToAdd
6954
) {
70-
return addUsedDeferredWords(usedDeferredWordsToAdd.stream());
55+
if (usedDeferredWords == null) {
56+
usedDeferredWords = ImmutableSet.builder();
57+
}
58+
usedDeferredWords.addAll(usedDeferredWordsToAdd);
59+
return this;
7160
}
7261

7362
public DeferredTokenBuilder addUsedDeferredWords(
7463
Stream<String> usedDeferredWordsToAdd
7564
) {
7665
if (usedDeferredWords == null) {
77-
usedDeferredWords = usedDeferredWordsToAdd;
78-
} else {
79-
usedDeferredWords = Stream.concat(usedDeferredWords, usedDeferredWordsToAdd);
66+
usedDeferredWords = ImmutableSet.builder();
8067
}
68+
usedDeferredWordsToAdd.forEach(usedDeferredWords::add);
8169
return this;
8270
}
8371

8472
public DeferredTokenBuilder addSetDeferredWords(
8573
Collection<String> setDeferredWordsToAdd
8674
) {
87-
return addSetDeferredWords(setDeferredWordsToAdd.stream());
75+
if (setDeferredWords == null) {
76+
setDeferredWords = ImmutableSet.builder();
77+
}
78+
setDeferredWords.addAll(setDeferredWordsToAdd);
79+
return this;
8880
}
8981

9082
public DeferredTokenBuilder addSetDeferredWords(
9183
Stream<String> setDeferredWordsToAdd
9284
) {
9385
if (setDeferredWords == null) {
94-
setDeferredWords = setDeferredWordsToAdd;
95-
} else {
96-
setDeferredWords = Stream.concat(setDeferredWords, setDeferredWordsToAdd);
86+
setDeferredWords = ImmutableSet.builder();
9787
}
88+
setDeferredWordsToAdd.forEach(setDeferredWords::add);
9889
return this;
9990
}
10091
}
@@ -103,9 +94,10 @@ public DeferredTokenBuilder addSetDeferredWords(
10394
// These words aren't yet DeferredValues, but are unresolved
10495
// so they should be replaced with DeferredValueImpls if they exist in the context
10596
private final Set<String> usedDeferredWords;
106-
97+
private final Set<String> usedDeferredBases;
10798
// These words are those which will be set to a value which has been deferred.
10899
private final Set<String> setDeferredWords;
100+
private final Set<String> setDeferredBases;
109101

110102
// Used to determine the combine scope
111103
private final CallStack macroStack;
@@ -211,23 +203,45 @@ public DeferredToken(
211203
) {
212204
this(
213205
token,
214-
getBases(usedDeferredWords),
215-
getBases(setDeferredWords),
206+
usedDeferredWords,
207+
setDeferredWords,
216208
acquireImportResourcePath(),
217209
acquireMacroStack()
218210
);
219211
}
220212

221213
private DeferredToken(
222214
Token token,
223-
Set<String> usedDeferredWordBases,
224-
Set<String> setDeferredWordBases,
215+
Set<String> usedDeferredWords,
216+
Set<String> setDeferredWords,
225217
String importResourcePath,
226218
CallStack macroStack
227219
) {
220+
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();
228221
this.token = token;
229-
this.usedDeferredWords = usedDeferredWordBases;
230-
this.setDeferredWords = setDeferredWordBases;
222+
this.usedDeferredBases =
223+
usedDeferredWords.isEmpty()
224+
? Collections.emptySet()
225+
: usedDeferredWords
226+
.stream()
227+
.map(DeferredToken::splitToken)
228+
.map(DeferredToken::getFirstNonEmptyToken)
229+
.distinct()
230+
.filter(word ->
231+
interpreter == null ||
232+
!(interpreter.getContext().get(word) instanceof DeferredMacroValueImpl)
233+
)
234+
.collect(Collectors.toSet());
235+
this.usedDeferredWords = usedDeferredWords;
236+
this.setDeferredBases =
237+
setDeferredWords.isEmpty()
238+
? Collections.emptySet()
239+
: setDeferredWords
240+
.stream()
241+
.map(DeferredToken::splitToken)
242+
.map(DeferredToken::getFirstNonEmptyToken)
243+
.collect(Collectors.toSet());
244+
this.setDeferredWords = setDeferredWords;
231245
this.importResourcePath = importResourcePath;
232246
this.macroStack = macroStack;
233247
}
@@ -240,10 +254,18 @@ public Set<String> getUsedDeferredWords() {
240254
return usedDeferredWords;
241255
}
242256

257+
public Set<String> getUsedDeferredBases() {
258+
return usedDeferredBases;
259+
}
260+
243261
public Set<String> getSetDeferredWords() {
244262
return setDeferredWords;
245263
}
246264

265+
public Set<String> getSetDeferredBases() {
266+
return setDeferredBases;
267+
}
268+
247269
public String getImportResourcePath() {
248270
return importResourcePath;
249271
}
@@ -255,7 +277,7 @@ public CallStack getMacroStack() {
255277
public void addTo(Context context) {
256278
addTo(
257279
context,
258-
usedDeferredWords
280+
usedDeferredBases
259281
.stream()
260282
.filter(word -> {
261283
Object value = context.get(word);
@@ -285,7 +307,7 @@ private void deferPropertiesOnContext(
285307
) {
286308
if (isInSameScope(context)) {
287309
// set props are only deferred when within the scope which the variable is set in
288-
markDeferredWordsAndFindSources(context, getSetDeferredWords(), true);
310+
markDeferredWordsAndFindSources(context, getSetDeferredBases(), true);
289311
}
290312
wordsWithoutDeferredSource.forEach(word -> deferDuplicatePointers(context, word));
291313
wordsWithoutDeferredSource.removeAll(
@@ -424,12 +446,4 @@ private static String getFirstNonEmptyToken(List<String> strings) {
424446
public static List<String> splitToken(String token) {
425447
return Arrays.asList(token.split("\\."));
426448
}
427-
428-
public static Set<String> getBases(Set<String> original) {
429-
return original
430-
.stream()
431-
.map(DeferredToken::splitToken)
432-
.map(prop -> prop.get(0))
433-
.collect(Collectors.toSet());
434-
}
435449
}

src/main/java/com/hubspot/jinjava/util/EagerReconstructionUtils.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -795,13 +795,13 @@ public static Map<String, String> handleDeferredTokenAndReconstructReferences(
795795
deferredToken.addTo(interpreter.getContext());
796796
return reconstructDeferredReferences(
797797
interpreter,
798-
deferredToken.getUsedDeferredWords()
798+
deferredToken.getUsedDeferredBases()
799799
);
800800
}
801801

802802
public static Map<String, String> reconstructDeferredReferences(
803803
JinjavaInterpreter interpreter,
804-
Set<String> usedDeferredWords
804+
Set<String> usedDeferredBases
805805
) {
806806
return interpreter
807807
.getContext()
@@ -815,7 +815,7 @@ public static Map<String, String> reconstructDeferredReferences(
815815
.filter(entry ->
816816
// Always reconstruct the DeferredLazyReferenceSource, but only reconstruct DeferredLazyReferences when they are used
817817
entry.getValue() instanceof DeferredLazyReferenceSource ||
818-
usedDeferredWords.contains(entry.getKey())
818+
usedDeferredBases.contains(entry.getKey())
819819
)
820820
.peek(entry -> ((OneTimeReconstructible) entry.getValue()).setReconstructed(true))
821821
.map(entry ->

src/test/java/com/hubspot/jinjava/lib/tag/eager/EagerSetTagTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ public void itDefersBlockWithFilter() {
159159
.stream()
160160
.flatMap(deferredToken -> deferredToken.getUsedDeferredWords().stream())
161161
.collect(Collectors.toSet())
162+
)
163+
.containsExactlyInAnyOrder("deferred", "foo", "add.filter");
164+
assertThat(
165+
context
166+
.getDeferredTokens()
167+
.stream()
168+
.flatMap(deferredToken -> deferredToken.getUsedDeferredBases().stream())
169+
.collect(Collectors.toSet())
162170
)
163171
.containsExactlyInAnyOrder("deferred", "foo", "add");
164172
}
@@ -211,6 +219,14 @@ public void itDefersInDeferredExecutionModeWithFilter() {
211219
.stream()
212220
.flatMap(deferredToken -> deferredToken.getUsedDeferredWords().stream())
213221
.collect(Collectors.toSet())
222+
)
223+
.containsExactlyInAnyOrder("deferred", "foo", "add.filter");
224+
assertThat(
225+
context
226+
.getDeferredTokens()
227+
.stream()
228+
.flatMap(deferredToken -> deferredToken.getUsedDeferredBases().stream())
229+
.collect(Collectors.toSet())
214230
)
215231
.containsExactlyInAnyOrder("deferred", "foo", "add");
216232
context.remove("foo");

src/test/java/com/hubspot/jinjava/util/DeferredValueUtilsTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,9 @@ public void itFindsFirstValidDeferredWords() {
256256
.addSetDeferredWords(ImmutableSet.of("deferred", ".attribute2"))
257257
.build();
258258

259-
assertThat(deferredToken.getUsedDeferredWords())
259+
assertThat(deferredToken.getUsedDeferredBases())
260260
.isEqualTo(ImmutableSet.of("deferred", "attribute1"));
261-
assertThat(deferredToken.getSetDeferredWords())
261+
assertThat(deferredToken.getSetDeferredBases())
262262
.isEqualTo(ImmutableSet.of("deferred", "attribute2"));
263263
}
264264

@@ -272,9 +272,9 @@ public void itFindsFirstValidDeferredWordsWithNestedAttributes() {
272272
.addSetDeferredWords(ImmutableSet.of("deferred", ".attribute2.ignoreme"))
273273
.build();
274274

275-
assertThat(deferredToken.getUsedDeferredWords())
275+
assertThat(deferredToken.getUsedDeferredBases())
276276
.isEqualTo(ImmutableSet.of("deferred", "attribute1"));
277-
assertThat(deferredToken.getSetDeferredWords())
277+
assertThat(deferredToken.getSetDeferredBases())
278278
.isEqualTo(ImmutableSet.of("deferred", "attribute2"));
279279
}
280280

0 commit comments

Comments
 (0)