/* * 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.prettytime; import static org.trimou.util.Checker.checkArgumentsNotNull; import java.util.Date; import java.util.Locale; import org.ocpsoft.prettytime.PrettyTime; import org.trimou.engine.cache.ComputingCache; import org.trimou.engine.config.Configuration; import org.trimou.engine.convert.Converter; import org.trimou.engine.convert.ObjectToDateConverter; import org.trimou.exception.MustacheException; import org.trimou.exception.MustacheProblem; import org.trimou.handlebars.Options; import org.trimou.handlebars.OptionsHashKeys; import org.trimou.handlebars.i18n.LocaleAwareValueHelper; import org.trimou.prettytime.resolver.PrettyTimeResolver; /** * <p> * Developers are encouraged to use this helper instead of * {@link PrettyTimeResolver} to avoid the negative performance impact during * interpolation. * </p> * * <p> * A {@link MustacheException} is thrown in case of the passed parameter is not * a formattable object (i.e. Date, Calendar or Long). * </p> * * <code> * {{pretty now}} * </code> * * <p> * Since 1.7 a custom {@link Locale} can be set via options hash with * {@link OptionsHashKeys#LOCALE} key. See also * {@link LocaleAwareValueHelper#getLocale(Options)}. * </p> * * <code> * {{pretty now locale='fr'}} * </code> * * @author Martin Kouba */ public class PrettyTimeHelper extends LocaleAwareValueHelper { public static final String COMPUTING_CACHE_CONSUMER_ID = PrettyTimeHelper.class .getName(); public static final String DEFAULT_NAME = "pretty"; private final PrettyTimeFactory factory; private final Converter<Object, Date> converter; /** * Lazy loading cache of PrettyTime instances */ private ComputingCache<Locale, PrettyTime> prettyTimeCache; public PrettyTimeHelper() { this(new DefaultPrettyTimeFactory()); } /** * * @param prettyTimeFactory */ public PrettyTimeHelper(PrettyTimeFactory prettyTimeFactory) { this(prettyTimeFactory, new ObjectToDateConverter()); } /** * * @param factory * @param converter */ private PrettyTimeHelper(PrettyTimeFactory factory, Converter<Object, Date> converter) { checkArgumentsNotNull(factory, converter); this.factory = factory; this.converter = converter; } @Override public void init(Configuration configuration) { super.init(configuration); prettyTimeCache = configuration.getComputingCacheFactory() .create(COMPUTING_CACHE_CONSUMER_ID, factory::createPrettyTime, null, 10L, null); } @Override public void execute(Options options) { Object param = options.getParameters().get(0); if (param == null) { throw new MustacheException( MustacheProblem.RENDER_HELPER_INVALID_OPTIONS, "PrettyTimeHelper - no instance to format [template: %s, line: %s, param: %s]", options.getTagInfo().getTemplateName(), options.getTagInfo().getLine()); } Date value = converter.convert(param); if (value == null) { throw new MustacheException( MustacheProblem.RENDER_HELPER_INVALID_OPTIONS, "Unable to get java.util.Date instance for PrettyTime [template: %s, line: %s, param: %s]", options.getTagInfo().getTemplateName(), options.getTagInfo().getLine(), param); } append(options, prettyTimeCache.get(getLocale(options)).format(value)); } /** * * @return a new builder */ public static Builder builder() { return new Builder(); } /** * * @author Martin Kouba * */ public static class Builder { private PrettyTimeFactory factory; private Converter<Object, Date> converter; /** * * @param factory * @return self * @see PrettyTimeFactory */ public Builder setFactory(PrettyTimeFactory factory) { this.factory = factory; return this; } /** * Might be useful to customize the conversion of the context object to * the {@link Date} instance. * * @param converter * @return self */ public Builder setConverter(Converter<Object, Date> converter) { this.converter = converter; return this; } public PrettyTimeHelper build() { if (factory == null) { factory = new DefaultPrettyTimeFactory(); } if (converter == null) { converter = new ObjectToDateConverter(); } return new PrettyTimeHelper(factory, converter); } } }