/*
* Copyright 2014 Martin Kouba
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.trimou.handlebars;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.trimou.engine.MustacheTagInfo;
import org.trimou.engine.MustacheTagType;
import org.trimou.util.Checker;
import org.trimou.util.Nested;
/**
* This helper works similarly as the JSP c:choose tag. It renders the content
* of the first <b>when</b> section whose first param is not falsy. If no
* <b>when</b> section is rendered, <b>otherwise</b> section is rendered, if
* present.
*
* <p>
* This helper should only contain <b>when</b> and <b>otherwise</b> sections.
* Other types of segments are always rendered.
* </p>
*
* <p>
* Since we push the flow object on the context stack, it's possible to refer
* the last context object with {@link Nested#up()} method. See also examples.
* </p>
*
* The following template:
*
* <pre>
* {{#choose}}
* {{#when up}}
* Hello active!
* {{/when}}
* {{#when foo}}
* Hello foo!
* {{/when}}
* {{#otherwise}}
* No match.
* {{/otherwise}}
* {{/choose}}
* </pre>
*
* will render "Hello active!" if last context object (i.e. "this" before we
* enter the choose helper) is not falsy. "Hello foo" if "this.up" is falsy and
* "foo" is not falsy. And "No match." if both "this.up" and "foo" are falsy.
*
* @author Martin Kouba
*/
public class ChooseHelper extends BasicSectionHelper {
private static final Logger LOGGER = LoggerFactory
.getLogger(ChooseHelper.class);
@Override
protected int numberOfRequiredParameters() {
return 0;
}
@Override
public void execute(Options options) {
options.push(new Flow(options.peek()));
options.fn();
options.pop();
}
@Override
public void validate(HelperDefinition definition) {
super.validate(definition);
Set<String> validNames = new HashSet<>(4);
for (Entry<String, Helper> entry : configuration.getHelpers()
.entrySet()) {
if (entry.getValue() instanceof WhenHelper
|| entry.getValue() instanceof OtherwiseHelper) {
validNames.add(entry.getKey());
}
}
for (MustacheTagInfo info : definition.getTagInfo().getChildTags()) {
if (!isValid(info, validNames)) {
LOGGER.warn(
"Invalid content detected {}. This helper should only contain when and otherwise sections. Other types of segments are always rendered!",
info);
}
}
}
private boolean isValid(MustacheTagInfo info, Set<String> validNames) {
if (!info.getType().equals(MustacheTagType.SECTION)) {
return false;
}
for (String name : validNames) {
if (info.getText().startsWith(name)) {
return true;
}
}
return false;
}
/**
* The first param is the test condition.
*
* @author Martin Kouba
*/
public static class WhenHelper extends BasicSectionHelper {
@Override
public void execute(Options options) {
Object contextObject = options.peek();
if (contextObject instanceof Flow) {
Flow flow = (Flow) contextObject;
if (!flow.isTerminated()
&& !Checker.isFalsy(options.getParameters().get(0))) {
options.fn();
flow.terminate();
}
} else {
throw Flow.newInvalidFlowException(options.getTagInfo());
}
}
}
public static class OtherwiseHelper extends BasicSectionHelper {
@Override
protected int numberOfRequiredParameters() {
return 0;
}
@Override
public void execute(Options options) {
Object conditionObject = options.peek();
if (conditionObject instanceof Flow) {
Flow condition = (Flow) conditionObject;
if (!condition.isTerminated()) {
options.fn();
condition.terminate();
}
} else {
throw Flow.newInvalidFlowException(options.getTagInfo());
}
}
}
}