/*
* Copyright 2013 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.engine.segment;
import java.io.IOException;
import java.util.List;
import org.trimou.annotations.Internal;
import org.trimou.engine.MustacheTagType;
import org.trimou.engine.context.ExecutionContext;
import org.trimou.engine.context.ValueWrapper;
import org.trimou.engine.convert.ValueConverter;
import org.trimou.engine.parser.Template;
import org.trimou.engine.text.TextSupport;
import org.trimou.exception.MustacheException;
import org.trimou.exception.MustacheProblem;
import org.trimou.lambda.Lambda;
import org.trimou.util.Strings;
/**
* Value segment (aka variable tag).
*
* @author Martin Kouba
*/
@Internal
public class ValueSegment extends AbstractSegment
implements HelperAwareSegment {
private final boolean unescape;
private final TextSupport textSupport;
private final HelperExecutionHandler helperHandler;
private final ValueProvider provider;
private final List<ValueConverter> converters;
/**
*
* @param text
* @param origin
* @param unescape
*/
public ValueSegment(String text, Origin origin, boolean unescape) {
super(text, origin);
this.unescape = unescape;
this.helperHandler = isHandlebarsSupportEnabled()
? HelperExecutionHandler.from(text, getEngine(), this) : null;
if (helperHandler == null) {
this.textSupport = getEngineConfiguration().getTextSupport();
this.provider = new ValueProvider(text, getEngineConfiguration());
if (getEngineConfiguration().getValueConverters().isEmpty()) {
this.converters = null;
} else {
this.converters = getEngineConfiguration().getValueConverters();
}
} else {
this.textSupport = null;
this.provider = null;
this.converters = null;
}
}
public SegmentType getType() {
return SegmentType.VALUE;
}
public boolean isUnescape() {
return unescape;
}
public Appendable execute(Appendable appendable, ExecutionContext context) {
if (helperHandler != null) {
return helperHandler.execute(appendable, context);
} else {
ValueWrapper value = provider.get(context);
try {
if (value.isNull()) {
Object replacement = getEngineConfiguration()
.getMissingValueHandler().handle(getTagInfo());
if (replacement != null) {
processValue(appendable, context, replacement);
}
} else {
processValue(appendable, context, value.get());
}
} finally {
value.release();
}
return appendable;
}
}
@Override
public Appendable fn(Appendable appendable, ExecutionContext context) {
// No-op
return appendable;
}
@Override
protected String getSegmentName() {
return getText();
}
@Override
protected MustacheTagType getTagType() {
// Because of one segment is used for both a variable and an unescape
// variable
return unescape ? MustacheTagType.UNESCAPE_VARIABLE
: MustacheTagType.VARIABLE;
}
private void processValue(Appendable appendable, ExecutionContext context,
Object value) {
if (value instanceof Lambda) {
processLambda(appendable, context, value);
} else {
writeValue(appendable, convertValue(value));
}
}
private String convertValue(Object value) {
if (converters != null) {
for (ValueConverter converter : converters) {
String result = converter.convert(value);
if (result != null) {
return result;
}
}
}
return value.toString();
}
private void writeValue(Appendable appendable, String text) {
if (unescape) {
append(appendable, text);
} else {
try {
textSupport.appendEscapedHtml(text, appendable);
} catch (IOException e) {
throw new MustacheException(MustacheProblem.RENDER_IO_ERROR, e);
}
}
}
private void processLambda(Appendable appendable, ExecutionContext context,
Object value) {
Lambda lambda = (Lambda) value;
String returnValue = lambda.invoke(null);
if (returnValue == null) {
Object replacement = getEngineConfiguration()
.getMissingValueHandler().handle(getTagInfo());
if (replacement != null) {
processValue(appendable, context, replacement);
return;
}
} else if (!returnValue.equals(Strings.EMPTY)) {
if (lambda.isReturnValueInterpolated()) {
// Parse and interpolate the return value
StringBuilder interpolated = new StringBuilder();
Template temp = (Template) getEngine().compileMustache(
Lambdas.constructLambdaOneoffTemplateName(this),
returnValue);
temp.getRootSegment().execute(interpolated, context);
writeValue(appendable, interpolated.toString());
} else {
writeValue(appendable, returnValue);
}
}
}
}