/* 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.riotfamily.common.web.macro; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.riotfamily.cachius.CacheContext; import org.riotfamily.common.beans.property.PropertyUtils; import org.riotfamily.common.ui.ObjectGroup; import org.riotfamily.common.util.FormatUtils; import org.riotfamily.common.util.Generics; import org.riotfamily.common.web.mvc.mapping.HandlerUrlResolver; import org.riotfamily.common.web.performance.ResourceStamper; import org.riotfamily.common.web.support.ServletUtils; import org.riotfamily.common.web.support.StringCapturingResponseWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.MessageSourceResolvable; import org.springframework.util.StringUtils; import org.springframework.web.servlet.support.RequestContextUtils; import freemarker.core.Environment; import freemarker.template.ObjectWrapper; import freemarker.template.TemplateDirectiveBody; import freemarker.template.TemplateDirectiveModel; import freemarker.template.TemplateException; import freemarker.template.TemplateModel; /** * @author Felix Gnass [fgnass at neteye dot de] * @since 6.5 */ public class CommonMacroHelper { private Logger log = LoggerFactory.getLogger(CommonMacroHelper.class); private static final Pattern LINK_PATTERN = Pattern.compile( "(\\s+href\\s*=\\s*\")(.+?)(\")", Pattern.CASE_INSENSITIVE); private static final Pattern TEXT_PATTERN = Pattern.compile( "([^<]*)(<[^>]+>|$)"); private static Random random = new Random(); private Date currentTime; private ApplicationContext ctx; private HttpServletRequest request; private HttpServletResponse response; private ServletContext servletContext; private ResourceStamper stamper; private HandlerUrlResolver handlerUrlResolver; private boolean compressResources; private Locale requestLocale = null; private String dateFormat; private String dateDelimiter; public CommonMacroHelper(ApplicationContext ctx, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, ResourceStamper stamper, HandlerUrlResolver handlerUrlResolver, boolean compressResources) { this.ctx = ctx; this.request = request; this.response = response; this.servletContext = servletContext; this.stamper = stamper; this.handlerUrlResolver = handlerUrlResolver; this.compressResources = compressResources; } public Random getRandom() { return random; } public boolean isCompressResources() { return compressResources; } public Date getCurrentTime() { if (currentTime == null) { currentTime = new Date(); } return currentTime; } public Locale getLocale() { if (requestLocale == null) { requestLocale = RequestContextUtils.getLocale(request); } return requestLocale; } public String getDateFormat() { if (dateFormat == null) { dateFormat = FormatUtils.getDateFormat(getLocale()); } return dateFormat; } public String getDateDelimiter() { if (dateDelimiter == null) { if (dateFormat == null) { getDateFormat(); } dateDelimiter = FormatUtils.getDateDelimiter(dateFormat); } return dateDelimiter; } public String getMessage(String code, Collection<?> args) { return ctx.getMessage(code.trim(), args.toArray(), null, getLocale()); } public String getMessageWithDefault(String code, String defaultMessage, Collection<?> args, Locale locale) { if (!StringUtils.hasText(defaultMessage)) { defaultMessage = null; } else { defaultMessage = FormatUtils.stripWhitespaces(defaultMessage); } return ctx.getMessage(code.trim(), args.toArray(), defaultMessage, locale); } public String getMessage(MessageSourceResolvable resolvable) { return ctx.getMessage(resolvable, getLocale()); } public String encodeUrl(String url) { return response.encodeURL(url); } public String resolveUrl(String url) { return ServletUtils.resolveUrl(url, request); } public String resolveAndEncodeUrl(String url) { return ServletUtils.resolveAndEncodeUrl(url, request, response); } public String localize(String path) { String ext = FormatUtils.getExtension(path); String prefix = path; if (ext.length() > 0) { ext = "." + ext; prefix = path.substring(0, path.length() - ext.length()) + "_"; } String localized = prefix + getLocale() + ext; if (fileExists(localized)) { return localized; } localized = prefix + getLocale().getLanguage() + ext; if (fileExists(localized)) { return localized; } return path; } private boolean fileExists(String path) { String realPath = servletContext.getRealPath(path); if (realPath == null) { return false; } return new File(realPath).exists(); } public String resolveAndEncodeLinks(String html) { Matcher m = LINK_PATTERN.matcher(html); StringBuffer result = new StringBuffer(); while (m.find() && m.groupCount() == 3) { String oldLink = m.group(2).trim(); String newLink = ServletUtils.resolveAndEncodeUrl(oldLink, request, response); log.debug("Replacing link '" + oldLink + "' with '" + newLink + "'"); m.appendReplacement(result, m.group(1) + newLink + m.group(3)); } m.appendTail(result); return result.toString(); } public String getAbsoluteUrl(String url) { return ServletUtils.getAbsoluteUrlPrefix(request) .append(request.getContextPath()).append(url).toString(); } public String getUrlForHandler(String handlerName, List<Object> attributes) { Object[] vars = attributes != null ? attributes.toArray() : null; return handlerUrlResolver.getUrlForHandler(handlerName, vars); } public String getOriginatingRequestUri() { String uri = ServletUtils.getOriginatingRequestUri(request); if (StringUtils.hasText(request.getQueryString())) { uri = uri + "?" + request.getQueryString(); } return uri; } public String getPathWithinApplication() { return ServletUtils.getPathWithinApplication(request); } public String addRequestParameters(String url) { return ServletUtils.addRequestParameters(url, request); } public boolean isExternalUrl(String url) { try { URI uri = new URI(url); if (!uri.isOpaque()) { if (uri.isAbsolute() && !request.getServerName().equals( uri.getHost())) { return true; } } } catch (URISyntaxException e) { log.warn(e.getMessage()); } return false; } public String getHostName(String url) { try { URI uri = new URI(url); return uri.getHost(); } catch (URISyntaxException e) { log.warn(e.getMessage()); } return null; } public String include(String url, boolean dynamic) throws ServletException, IOException { if (dynamic && CacheContext.exists()) { return "(@riot.include " + url + ")"; } return capture(url); } public String capture(String url) throws ServletException, IOException { StringCapturingResponseWrapper wrapper = new StringCapturingResponseWrapper(response); request.getRequestDispatcher(url).include(request, wrapper); return wrapper.getCapturedData(); } public String addTimestamp(String s) { return stamper.stamp(s); } public String addCurrentTimestamp(String s) { return stamper.stamp(s, true); } /** * Partitions the given collection by inspecting the specified property * of the contained items. * * @param c The collection to partition * @param titleProperty The property to use for grouping * @return A list of {@link ObjectGroup ObjectGroups} */ public<T> List<ObjectGroup<?, T>> partition(Collection<T> c, String titleProperty) { ArrayList<ObjectGroup<?, T>> groups = Generics.newArrayList(); ObjectGroup<Object, T> group = null; for (T item : c) { Object title = PropertyUtils.getProperty(item, titleProperty); if (group == null || (title != null && !title.equals(group.getTitle()))) { group = ObjectGroup.newInstance(title, item, false); groups.add(group); } else { group.add(item); } } return groups; } public String toDelimitedString(Collection<?> c, String delim) { return StringUtils.collectionToDelimitedString(c, delim); } /** * Shuffles the given collection * * @param collection The collection to shuffle * @return The shuffled collection */ public List<?> shuffle(Collection<?> collection) { List<?> result = new ArrayList<Object>(collection); Collections.shuffle(result); return result; } public String formatNumber(Number number, String pattern, String localeString) { Locale locale = StringUtils.hasText(localeString) ? StringUtils.parseLocaleString(localeString) : Locale.US; return FormatUtils.formatNumber(number, pattern, locale); } public int round(float number) { return Math.round(number); } /** * Splits a list into a specified number of groups. The items are * distributed evenly. Example: * <pre> * 1 | 4 | 7 * 2 | 5 | 8 * 3 | 6 * </pre> * @param items The items to split * @param groups The number of groups (NOT number of group-items) * @return The splitted list */ public<T> List<List<T>> split(List<T> items, int groups) { if (items == null) { return null; } List<List<T>> result = new ArrayList<List<T>>(); int itemsSize = items.size(); int remainder = itemsSize % groups; int maxGroupMembers = itemsSize / groups; if (remainder > 0) { ++maxGroupMembers; } int currentIndex = 0; for (int i = 0; i < groups; i++) { int currentGroupMembers = maxGroupMembers; if (remainder > 0 && i >= remainder) { --currentGroupMembers; } List<T> currentList = new ArrayList<T>(); for (int j = 0; j < currentGroupMembers; j++) { currentList.add(items.get(currentIndex++)); } result.add(currentList); } return result; } public ExposeAsVariablesDirective getExposeAsVariablesDirective() { return new ExposeAsVariablesDirective(); } public static class ExposeAsVariablesDirective implements TemplateDirectiveModel { @SuppressWarnings("unchecked") public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { Object param = params.values().iterator().next(); if (param instanceof Map) { ObjectWrapper objectWrapper = env.getConfiguration().getObjectWrapper(); Map map = (Map) param; Iterator it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); TemplateModel model = objectWrapper.wrap(entry.getValue()); env.setVariable((String) entry.getKey(), model); } } } } }