Skip to content

Commit 9eaf68e

Browse files
committed
Add recursive reconstruction logic
1 parent 4ba66d8 commit 9eaf68e

2 files changed

Lines changed: 100 additions & 38 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ private static CallStack acquireMacroStack() {
250250
.orElse(null);
251251
}
252252

253-
private static Set<String> getBases(Set<String> original) {
253+
public static Set<String> getBases(Set<String> original) {
254254
return original
255255
.stream()
256256
.map(prop -> prop.split("\\.", 2)[0])

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

Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,40 @@ public static Map<String, String> reconstructFromContextBeforeDeferringAsMap(
128128
Set<String> deferredWords,
129129
JinjavaInterpreter interpreter
130130
) {
131-
Map<String, String> reconstructedValues = new LinkedHashMap<>();
132-
reconstructedValues.putAll(
133-
reconstructMacroFunctionsBeforeDeferring(deferredWords, interpreter)
131+
PrefixToPreserveState prefixToPreserveState = new PrefixToPreserveState();
132+
reconstructFromContextBeforeDeferringAsMap(
133+
prefixToPreserveState,
134+
deferredWords,
135+
interpreter,
136+
0
134137
);
135-
Set<String> deferredWordBases = filterToRelevantBases(deferredWords, interpreter);
138+
return prefixToPreserveState;
139+
}
136140

137-
reconstructedValues.putAll(
138-
reconstructSetVariablesBeforeDeferring(deferredWordBases, interpreter)
139-
);
140-
return reconstructedValues;
141+
private static void reconstructFromContextBeforeDeferringAsMap(
142+
PrefixToPreserveState prefixToPreserveState,
143+
Set<String> deferredWords,
144+
JinjavaInterpreter interpreter,
145+
int depth
146+
) {
147+
if (depth <= interpreter.getConfig().getMaxRenderDepth()) {
148+
reconstructMacroFunctionsBeforeDeferring(
149+
prefixToPreserveState,
150+
deferredWords,
151+
interpreter
152+
);
153+
Set<String> deferredWordBases = filterToRelevantBases(deferredWords, interpreter);
154+
if (deferredWordBases.isEmpty()) {
155+
return;
156+
}
157+
158+
reconstructSetVariablesBeforeDeferring(
159+
prefixToPreserveState,
160+
deferredWordBases,
161+
interpreter,
162+
depth
163+
);
164+
}
141165
}
142166

143167
private static Set<String> filterToRelevantBases(
@@ -184,13 +208,15 @@ private static Set<String> filterToRelevantBases(
184208
* @return A jinjava-syntax string that is the images of any macro functions that must
185209
* be evaluated at a later time.
186210
*/
187-
private static Map<String, String> reconstructMacroFunctionsBeforeDeferring(
211+
private static void reconstructMacroFunctionsBeforeDeferring(
212+
PrefixToPreserveState prefixToPreserveState,
188213
Set<String> deferredWords,
189214
JinjavaInterpreter interpreter
190215
) {
191216
Set<String> toRemove = new HashSet<>();
192217
Map<String, MacroFunction> macroFunctions = deferredWords
193218
.stream()
219+
.filter(w -> !prefixToPreserveState.containsKey(w))
194220
.filter(w -> !interpreter.getContext().containsKey(w))
195221
.map(w -> interpreter.getContext().getGlobalMacro(w))
196222
.filter(Objects::nonNull)
@@ -230,37 +256,38 @@ private static Map<String, String> reconstructMacroFunctionsBeforeDeferring(
230256
.collect(
231257
Collectors.toMap(Entry::getKey, entry -> entry.getValue().asTemplateString())
232258
);
259+
prefixToPreserveState.withAll(reconstructedMacros);
233260
// Remove macro functions from the set because they've been fully processed now.
234261
deferredWords.removeAll(toRemove);
235-
return reconstructedMacros;
236262
}
237263

238-
private static Map<String, String> reconstructSetVariablesBeforeDeferring(
264+
private static void reconstructSetVariablesBeforeDeferring(
265+
PrefixToPreserveState prefixToPreserveState,
239266
Set<String> deferredWords,
240-
JinjavaInterpreter interpreter
267+
JinjavaInterpreter interpreter,
268+
int depth
241269
) {
242-
if (deferredWords.isEmpty()) {
243-
return Collections.emptyMap();
244-
}
245270
Set<String> metaContextVariables = interpreter.getContext().getMetaContextVariables();
246-
return deferredWords
271+
deferredWords
247272
.stream()
273+
.filter(w -> !metaContextVariables.contains(w))
274+
.filter(w -> !prefixToPreserveState.containsKey(w))
275+
.map(
276+
word ->
277+
new AbstractMap.SimpleImmutableEntry<>(word, interpreter.getContext().get(word))
278+
)
248279
.filter(
249-
w ->
250-
interpreter.getContext().containsKey(w) &&
251-
!(interpreter.getContext().get(w) instanceof DeferredValue)
280+
entry -> entry.getValue() != null && !(entry.getValue() instanceof DeferredValue)
252281
)
253-
.filter(w -> !metaContextVariables.contains(w))
254-
.collect(
255-
Collectors.toMap(
256-
Function.identity(),
257-
word ->
258-
buildBlockOrInlineSetTag(
259-
word,
260-
interpreter.getContext().get(word),
261-
interpreter
262-
)
263-
)
282+
.forEach(
283+
entry ->
284+
buildBlockOrInlineSetTagRecursively(
285+
prefixToPreserveState,
286+
entry.getKey(),
287+
entry.getValue(),
288+
interpreter,
289+
depth
290+
)
264291
);
265292
}
266293

@@ -280,7 +307,47 @@ public static String buildBlockOrInlineSetTagAndRegisterDeferredToken(
280307
return buildBlockOrInlineSetTag(name, value, interpreter, true);
281308
}
282309

283-
public static String buildBlockOrInlineSetTag(
310+
private static void buildBlockOrInlineSetTagRecursively(
311+
PrefixToPreserveState prefixToPreserveState,
312+
String name,
313+
Object value,
314+
JinjavaInterpreter interpreter,
315+
int depth
316+
) {
317+
if (value instanceof DeferredValue || value instanceof PyishBlockSetSerializable) {
318+
prefixToPreserveState.put(
319+
name,
320+
buildBlockOrInlineSetTag(name, value, interpreter, false)
321+
);
322+
return;
323+
}
324+
String pyishStringRepresentation = PyishObjectMapper.getAsPyishString(value);
325+
326+
if (
327+
depth > 0 &&
328+
depth < interpreter.getConfig().getMaxRenderDepth() &&
329+
interpreter.getConfig().isNestedInterpretationEnabled()
330+
) {
331+
Set<String> dependentWords = EagerExpressionResolver.findDeferredWords(
332+
pyishStringRepresentation,
333+
interpreter
334+
);
335+
if (!dependentWords.isEmpty()) {
336+
reconstructFromContextBeforeDeferringAsMap(
337+
prefixToPreserveState,
338+
dependentWords,
339+
interpreter,
340+
depth + 1
341+
);
342+
}
343+
}
344+
prefixToPreserveState.put(
345+
name,
346+
buildSetTag(ImmutableMap.of(name, pyishStringRepresentation), interpreter, false)
347+
);
348+
}
349+
350+
private static String buildBlockOrInlineSetTag(
284351
String name,
285352
Object value,
286353
JinjavaInterpreter interpreter,
@@ -300,13 +367,8 @@ public static String buildBlockOrInlineSetTag(
300367
registerDeferredToken
301368
);
302369
}
303-
String pyishStringRepresentation = PyishObjectMapper.getAsPyishString(value);
304-
Set<String> dependentWords = EagerExpressionResolver.findDeferredWords(
305-
pyishStringRepresentation,
306-
interpreter
307-
);
308370
return buildSetTag(
309-
ImmutableMap.of(name, pyishStringRepresentation),
371+
ImmutableMap.of(name, PyishObjectMapper.getAsPyishString(value)),
310372
interpreter,
311373
registerDeferredToken
312374
);

0 commit comments

Comments
 (0)