/* * Copyright 2015 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 static org.trimou.handlebars.OptionsHashKeys.DELIMITER; import static org.trimou.handlebars.OptionsHashKeys.LAMBDA; import java.lang.reflect.Array; import java.util.Iterator; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.trimou.exception.MustacheException; import org.trimou.exception.MustacheProblem; import org.trimou.lambda.Lambda; import org.trimou.lambda.Lambda.InputType; import org.trimou.util.ImmutableSet; /** * This helper takes all the objects specified as the parameters and joins the * {@link Object#toString()} values together with the specified delimiter * (optional). Elements of {@link Iterable}s and arrays are treated as separate * objects. * * <p> * The following template will render "Hello Martin, John!" for the list of * strings "Martin" and "John": * </p> * * <pre> * Hello {{join listOfNames delimiter=", " }}! * </pre> * * <p> * Note that the output is escaped (if needed). It's possible to use unescape * variable though, e.g.: * </p> * * <pre> * Hello {{&join "<strong>Martin</strong>" "John" delimiter=", " }} * </pre> * * <p> * An optional lambda may be applied to each value. Note that the * lambda is always processed as with {@link InputType#LITERAL} and * {@link Lambda#isReturnValueInterpolated()} set to false. * </p> * * <pre> * Hello {{&join "Martin" "John" delimiter=" " lambda=makeItalic}} * </pre> * * @author Martin Kouba */ public class JoinHelper extends BasicValueHelper { private static final Logger LOGGER = LoggerFactory .getLogger(JoinHelper.class); @Override public void execute(Options options) { final Object delimiter = options.getHash().get(DELIMITER); final Lambda lambda = initLambda(options); if (options.getParameters().size() == 1) { processValue(options, options.getParameters().get(0), delimiter, lambda); } else { for (Iterator<Object> iterator = options.getParameters().iterator(); iterator .hasNext();) { Object value = iterator.next(); processValue(options, value, delimiter, lambda); if (iterator.hasNext() && delimiter != null) { append(options, delimiter, null); } } } } @Override protected Set<String> getSupportedHashKeys() { return ImmutableSet.of(DELIMITER, LAMBDA); } private void processValue(Options options, Object value, Object delimiter, Lambda lambda) { if (value == null) { return; } else if (value instanceof Iterable) { processIterable(options, (Iterable<?>) value, delimiter, lambda); } else if (value.getClass().isArray()) { processArray(options, value, delimiter, lambda); } else { append(options, value, lambda); } } @SuppressWarnings("rawtypes") private void processIterable(Options options, Iterable iterable, Object delimiter, Lambda lambda) { Iterator iterator = iterable.iterator(); if (!iterator.hasNext()) { return; } while (iterator.hasNext()) { append(options, iterator.next(), lambda); if (delimiter != null && iterator.hasNext()) { append(options, delimiter, null); } } } private void processArray(Options options, Object array, Object delimiter, Lambda lambda) { int length = Array.getLength(array); if (length < 1) { return; } for (int i = 0; i < length; i++) { append(options, Array.get(array, i), lambda); if (delimiter != null && (i + 1 < length)) { append(options, delimiter, null); } } } private void append(Options options, Object value, Lambda lambda) { append(options, lambda != null ? lambda.invoke(value.toString()) : convertValue(value)); } private Lambda initLambda(Options options) { final Object lambdaReference = options.getHash().get(LAMBDA); if (lambdaReference == null) { return null; } if (lambdaReference instanceof Lambda) { Lambda lambda = (Lambda) lambdaReference; if (lambda.isReturnValueInterpolated() || lambda.getInputType().equals(InputType.PROCESSED)) { LOGGER.warn( "The lambda is processed as with InputType#LITERAL and Lambda#isReturnValueInterpolated() set to false [{}]", options.getTagInfo()); } return lambda; } else { throw new MustacheException( MustacheProblem.RENDER_HELPER_INVALID_OPTIONS, "%s is not a valid Lambda reference [%s]", lambdaReference, options.getTagInfo()); } } }