/* * Copyright (C) 2014 the original author or authors. * * 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 ro.pippo.groovy; import groovy.text.markup.BaseTemplate; import groovy.text.markup.MarkupTemplateEngine; import groovy.text.markup.TemplateConfiguration; import java.io.IOException; import java.net.URL; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import org.ocpsoft.prettytime.PrettyTime; import ro.pippo.core.Languages; import ro.pippo.core.Messages; import ro.pippo.core.PippoConstants; import ro.pippo.core.PippoRuntimeException; import ro.pippo.core.route.ClasspathResourceHandler; import ro.pippo.core.route.PublicResourceHandler; import ro.pippo.core.route.Router; import ro.pippo.core.route.WebjarsResourceHandler; import ro.pippo.core.util.StringUtils; /** * Base class for Pippo Groovy Templates. * * @author James Moger */ public abstract class PippoGroovyTemplate extends BaseTemplate { private final Map<String, String> modelTypes; private final MarkupTemplateEngine engine; Router router; String language; Locale locale; Languages languages; Messages messages; PrettyTime prettyTime; AtomicReference<String> webjarsPatternRef; AtomicReference<String> publicPatternRef; public PippoGroovyTemplate(final MarkupTemplateEngine templateEngine, final Map model, final Map<String, String> modelTypes, final TemplateConfiguration configuration) { super(templateEngine, model, modelTypes, configuration); this.modelTypes = modelTypes; this.engine = templateEngine; this.webjarsPatternRef = new AtomicReference<>(); this.publicPatternRef = new AtomicReference<>(); } @SuppressWarnings("unchecked") public void setup(Languages languages, Messages messages, Router router) { this.languages = languages; this.messages = messages; this.router = router; // set global template variables getModel().put("contextPath", router.getContextPath()); getModel().put("appPath", router.getApplicationPath()); String language = (String) getModel().get(PippoConstants.REQUEST_PARAMETER_LANG); if (StringUtils.isNullOrEmpty(language)) { language = languages.getLanguageOrDefault(language); } this.language = language; // prepare the locale Locale locale = (Locale) getModel().get(PippoConstants.REQUEST_PARAMETER_LOCALE); if (locale == null) { locale = languages.getLocaleOrDefault(language); } this.locale = locale; } public void ln() throws IOException { newLine(); } public String webjarsAt(String path) { return classpathResourceAt(path, webjarsPatternRef, WebjarsResourceHandler.class); } public String publicAt(String path) { return classpathResourceAt(path, publicPatternRef, PublicResourceHandler.class); } private String classpathResourceAt(String path, AtomicReference<String> patternRef, Class<? extends ClasspathResourceHandler> resourceHandlerClass) { if (patternRef.get() == null) { String pattern = router.uriPatternFor(resourceHandlerClass); if (pattern == null) { throw new PippoRuntimeException("You must register a route for {}", resourceHandlerClass.getSimpleName()); } patternRef.set(pattern); } Map<String, Object> parameters = new HashMap<>(); parameters.put(ClasspathResourceHandler.PATH_PARAMETER, path); return router.uriFor(patternRef.get(), parameters); } public String i18n(String messageKey) throws IOException { return messages.get(messageKey, language); } public String i18n(String messageKey, Object... args) throws IOException { return messages.get(messageKey, language, args); } public String formatTime(Object input, String styleOrPattern) { Date date = getDateObject(input); DateFormat df; int type = parseStyle(styleOrPattern); if (type == -1) { df = new SimpleDateFormat(styleOrPattern, locale); } else { df = DateFormat.getDateTimeInstance(type, type, locale); } return df.format(date); } private Date getDateObject(Object value) { if (value instanceof Date) { return (Date) value; } else if (value instanceof Calendar) { return ((Calendar) value).getTime(); } else if (value instanceof Long) { return new Date((Long) value); } else { throw new PippoRuntimeException("Failed to get a date object from {}!", value); } } protected Integer parseStyle(String style) { if ("full".equals(style)) { return DateFormat.FULL; } else if ("long".equals(style)) { return DateFormat.LONG; } else if ("short".equals(style)) { return DateFormat.SHORT; } else if ("medium".equals(style)) { return DateFormat.MEDIUM; } else { return -1; } } public String prettyTime(Object input) { if (prettyTime == null) { this.prettyTime = new PrettyTime(locale); } Date date = getDateObject(input); return prettyTime.format(date); } public String ng(String content) throws IOException { return "{{ " + content + " }}"; } /** * Imports a template and renders it using the specified model, allowing * fine grained composition of templates and layouting. This works * similarily to a template include but allows a distinct model to be used. * If the layout inherits from the parent model, a new model is created, * with the values from the parent model, eventually overriden with those * provided specifically for this layout. * * @param model * model to be passed to the template * @param templateName * the name of the template to be used as a layout * @param inheritModel * a boolean indicating if we should inherit the parent model * @return this template instance * @throws IOException * @throws ClassNotFoundException */ @Override public Object layout(Map model, String templateName, boolean inheritModel) throws IOException, ClassNotFoundException { Map submodel = inheritModel ? forkModel(model) : model; URL resource = engine.resolveTemplate(templateName); PippoGroovyTemplate template = (PippoGroovyTemplate) engine .createTypeCheckedModelTemplate(resource, modelTypes).make(submodel); template.setup(languages, messages, router); template.writeTo(getOut()); return this; } @SuppressWarnings("unchecked") private Map forkModel(Map m) { Map result = new HashMap(); result.putAll(getModel()); result.putAll(m); return result; } }