Skip to content

Commit

Permalink
At last! Found it. What I hope is the last of <t:else> related bugs.
Browse files Browse the repository at this point in the history
  • Loading branch information
brett-smith committed Mar 14, 2024
1 parent 5917c20 commit 0356d6b
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 14 deletions.
64 changes: 51 additions & 13 deletions src/main/java/com/sshtools/tinytemplate/Templates.java
Original file line number Diff line number Diff line change
Expand Up @@ -751,25 +751,34 @@ private final static class Block {
final VariableExpander expander;
final Reader reader;
final String scope;
final int depth;

State state = State.START;
boolean match = true;
boolean capture = false;
boolean inElse;
int nestDepth = 0;

Block(TemplateModel model, VariableExpander expander, Reader reader/* , String scope */) {
this(model, expander, reader, null, true);
Block(int depth, TemplateModel model, VariableExpander expander, Reader reader/* , String scope */) {
this(depth, model, expander, reader, null, true);
}

Block(TemplateModel model, VariableExpander expander, Reader reader, String scope, boolean match) {
Block(int depth, TemplateModel model, VariableExpander expander, Reader reader, String scope, boolean match) {
this.model = model;
this.depth = depth;
this.match = match;
this.expander = expander;
this.reader = reader;
this.scope = scope;
}

@Override
public String toString() {
return "Block [scope=" + scope + ", depth=" + depth + ", state=" + state + ", match=" + match
+ ", capture=" + capture + ", inElse=" + inElse + ", nestDepth=" + nestDepth + "]";
}


}

private TemplateProcessor(Builder bldr) {
Expand All @@ -781,7 +790,7 @@ private TemplateProcessor(Builder bldr) {
}

public String process(TemplateModel model) {
var block = new Block(model, getExpanderForModel(model), model.text(false));
var block = new Block(0, model, getExpanderForModel(model), model.text(false));
read(block);

return block.out.toString();
Expand All @@ -802,12 +811,19 @@ public ResourceBundle get() {
};
}).collect(Collectors.toList())).withVariableSupplier(model::variable)
.withConditionEvaluator(cond -> conditionOrVariable(model, cond, "").orElseGet(() -> {
logger.ifPresent(l -> l.debug("Missing condition {0}, assuming {1}", cond, false));
logger.ifPresent(l -> l.debug(formatDebug(null, "Missing condition {0}, assuming {1}"), cond, false));
return false;
})).build();
});
return exp;
}

private String formatDebug(Block block, String message) {
if(block == null || block.depth == 0)
return message;
else
return String.format("%" + (block.depth * 4) + "s%s", "", message);
}

private void read(Block block) {
int r;
Expand All @@ -825,7 +841,6 @@ private void read(Block block) {
continue;
}

//var process = !block.capture && ((block.match && !block.inElse) || (block.match && block.inElse));
var process = !block.capture && block.match;

switch (block.state) {
Expand Down Expand Up @@ -890,6 +905,10 @@ else if (ch == 't') {
case T_TAG_NAME:
if (ch == '>') {
var directive = buf.toString().substring(1).trim();

if(logger.isPresent())
logger.get().debug(formatDebug(block, "Tag found {0}. Process {1}"), directive, process);

if(process) {
if(processDirective(block, directive)) {
buf.setLength(0);
Expand Down Expand Up @@ -938,8 +957,12 @@ else if (ch == 't') {
if(isNest) {
block.nestDepth--;
}

if(logger.isPresent())
logger.get().debug(formatDebug(block, "Tag end {0}. Process {1}"), directive, process);

if(directive.equals(block.scope) && (!isNest || (isNest && block.nestDepth == 0))) {
logger.ifPresent(lg -> lg.debug("Leaving scope {0}", block.scope));
logger.ifPresent(lg -> lg.debug(formatDebug(block, "Leaving scope {0}"), block.scope));
return;
} else {
if(directive.equals(block.scope) && isNest && process) {
Expand All @@ -962,10 +985,17 @@ else if (ch == 't') {
directive = directive.substring(0, directive.length() - 1);

var aboutToProcess = !process && directive.equals("t:else");
var wasInElse = block.inElse;

if(logger.isPresent())
logger.get().debug(formatDebug(block, "Leaf end {0}. Process {1}, About to Process {2}. In Else {3}. Match {4}"), directive, process, aboutToProcess, block.inElse, block.match);

if (process || aboutToProcess) {
directive = directive.trim();
if(processDirective(block, directive)) {
if(aboutToProcess && !process && wasInElse) {
block.match = false;
}
buf.setLength(0);
}
else {
Expand Down Expand Up @@ -998,6 +1028,10 @@ private boolean processDirective(Block block, String directive) {
var dir = ( spc == -1 ? directive : directive.substring(0, spc)).trim();
var var = ( spc == -1 ? null : directive.substring(spc + 1).trim());

if(logger.isPresent())
logger.get().debug(formatDebug(block, "Process directive {0}. Var {1}"), dir, var);


if(dir.equals("t:if")) {

var condition = Condition.parse(var);
Expand All @@ -1019,7 +1053,10 @@ private boolean processDirective(Block block, String directive) {
if(condition.negate)
match = !match;

var ifBlock = new Block(block.model, getExpanderForModel(block.model), block.model.text(true), "if", match);
if(logger.isPresent())
logger.get().debug(formatDebug(block, "Condition {0} evaluates to {1}"), var, match);

var ifBlock = new Block(block.depth + 1, block.model, getExpanderForModel(block.model), block.model.text(true), "if", match);
ifBlock.nestDepth = 1;

read(ifBlock);
Expand All @@ -1040,7 +1077,8 @@ else if(dir.equals("t:include")) {
return false;
} else {
var include = includeModel.get();
var incBlock = new Block(include, getExpanderForModel(include), include.text(true));
var incBlock = new Block(block.depth + 1,include, getExpanderForModel(include), include.text(true));
logger.ifPresent(l -> l.debug(formatDebug(incBlock, "** Including template {0} **"), var));
read(incBlock);
block.out.append(incBlock.out.toString());
block.state = State.START;
Expand All @@ -1063,7 +1101,7 @@ else if(dir.equals("t:object")) {
return false;
}
else {
var templBlock = new Block(block.model, block.expander, block.reader, "object", true);
var templBlock = new Block(block.depth + 1,block.model, block.expander, block.reader, "object", true);
templBlock.nestDepth = 1;
templBlock.capture = true;
read(templBlock);
Expand All @@ -1072,7 +1110,7 @@ else if(dir.equals("t:object")) {
var was = templ.parent;
try {
templ.parent = Optional.of(block.model);
var listBlock = new Block(templ, getExpanderForModel(templ), templ.text(true));
var listBlock = new Block(block.depth + 1,templ, getExpanderForModel(templ), templ.text(true));
read(listBlock);
block.out.append(listBlock.out.toString());
}
Expand All @@ -1095,7 +1133,7 @@ else if(dir.equals("t:list")) {
}
else {
/* Temporary block to read all the content we must repeat */
var tempBlock = new Block(block.model, block.expander, block.reader, "list", true);
var tempBlock = new Block(block.depth + 1,block.model, block.expander, block.reader, "list", true);
tempBlock.nestDepth = 1;
tempBlock.capture = true;
read(tempBlock);
Expand All @@ -1109,7 +1147,7 @@ else if(dir.equals("t:list")) {
templ.variable("_index", index);
templ.variable("_first", index == 0);
templ.variable("_last", index == templates.size() - 1);
var listBlock = new Block(templ, getExpanderForModel(templ), templ.text(true));
var listBlock = new Block(block.depth + 1,templ, getExpanderForModel(templ), templ.text(true));
read(listBlock);
block.out.append(listBlock.out.toString());
}
Expand Down
89 changes: 88 additions & 1 deletion src/test/java/com/sshtools/tinytemplate/TemplatesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,79 @@ public void testTemplateElseWithNestedIfThatsTrue() {
variable("label.class", "label-class")));
}


@Test
public void testTemplateThreeDeep() {
Assertions.assertEquals(
"<div class=\"form-floating input-group\">\n" +
" <textarea>Some content with blahg</textarea>\n" +
" <labelW for=\"12345\" class=\"label-class\">Some label</label>\n" +
" </div>\n" +
" \n" +
" \n" +
"\n" +
"\n" +
" <div id=\"zzzzzHelp\" class=\"form-text text-muted\">Some help</div>",
createParser().process(TemplateModel.ofContent("""
<t:if label>
<t:if label.floating>
<t:if input.group>
<div class="form-floating input-group">
<t:include input/>
<labelW for="${input.id}" class="${label.class}">${label}</label>
</div>
<t:else/>
<div class="form-floating">
<t:include input/>
<labelR for="${input.id}" class="${label.class}">${label}</label>
</div>
</t:if>
<t:else/>
<t:if input.group>
<div class="input-group">
<t:if label.first>
<labelX for="${input.id}" class="${label.class}">${label}</label>
</t:if>
<t:include input/>
<t:if !label.first>
<labelY for="${input.id}" class="${label.class}">${label}</label>
</t:if>
</div>
<t:else/>
<t:if label.first>
<labelZ for="${input.id}" class="${label.class}">${label}</label>
</t:if>
<t:include input/>
<t:if !label.first>
<labelQ for="${input.id}" class="${label.class}">${label}</label>
</t:if>
</t:if>
</t:if>
<t:else/>
<t:if input.group>
<div class="input-group">
<t:include input/>
</div>
<t:else/>
<t:include input/>
</t:if>
</t:if>
<t:if help>
<div id="${id}Help" class="form-text text-muted">${help}</div>
</t:if>
""").
variable("label", "Some label").
variable("help", "Some help").
variable("input.id", "12345").
include("input", TemplateModel.ofContent("<textarea>Some content with ${aVar}</textarea>").variable("aVar", "blahg")).
variable("id", "zzzzz").
condition("has.label", true).
condition("label.first", false).
condition("input.group", true).
condition("label.floating", true).
variable("label.class", "label-class")).trim());
}

@Test
public void testTemplateNestedIf3() {
Assertions.assertEquals("""
Expand Down Expand Up @@ -1042,7 +1115,21 @@ public void testTemplateObject() {
}

private TemplateProcessor createParser() {
return new TemplateProcessor.Builder().build();
return new TemplateProcessor.Builder().
withLogger(new Logger() {

@Override
public void warning(String message, Object... args) {
System.out.println(MessageFormat.format("[WARN] " + message, args));

}

@Override
public void debug(String message, Object... args) {
System.out.println(MessageFormat.format("[DEBUG] " + message, args));
}
}).
build();
}

public static Map<String, ?> createVars() {
Expand Down

0 comments on commit 0356d6b

Please sign in to comment.