/** * Copyright © 2014 Instituto Superior Técnico * * This file is part of FenixEdu CMS. * * FenixEdu CMS is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * FenixEdu CMS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with FenixEdu CMS. If not, see <http://www.gnu.org/licenses/>. */ package org.fenixedu.cms.rendering; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.fenixedu.bennu.core.i18n.BundleUtil; import org.fenixedu.bennu.portal.servlet.LazyForTokenParser; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import com.google.common.base.Preconditions; import com.google.common.collect.ContiguousSet; import com.google.common.collect.DiscreteDomain; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Range; import com.google.common.collect.Sets; import com.mitchellbosecke.pebble.extension.AbstractExtension; import com.mitchellbosecke.pebble.extension.Filter; import com.mitchellbosecke.pebble.extension.Function; import com.mitchellbosecke.pebble.extension.Test; import com.mitchellbosecke.pebble.tokenParser.TokenParser; public class CMSExtensions extends AbstractExtension { public class LengthFilter implements Filter { @Override public List<String> getArgumentNames() { return ImmutableList.of("collection"); } @Override public Object apply(Object input, Map<String, Object> args) { if (input != null) { if (input.getClass().isArray()) { return Array.getLength(input); } else if (input instanceof Collection) { return ((Collection<?>) input).size(); } } return 0; } } public class MapEntriesFunction implements Function { @Override public List<String> getArgumentNames() { return ImmutableList.of("map"); } @Override public Object execute(Map<String, Object> args) { if (args.get("map") != null && args.get("map") instanceof Map) { return ((Map<?, ?>) args.get("map")).entrySet(); } return Sets.newLinkedHashSet(); } } public class DateTimeFormaterFilter implements Filter { @Override public List<String> getArgumentNames() { return ImmutableList.of("format"); } @Override public Object apply(Object input, Map<String, Object> args) { if (input instanceof DateTime) { String pattern = (String) args.get("format"); if (pattern == null) { pattern = "MMMM d, Y, H:m"; } DateTimeFormatter fmt = DateTimeFormat.forPattern(pattern); return fmt.print((DateTime) input); } return ""; } } public class IterableHeadFilter implements Filter { @Override public List<String> getArgumentNames() { return ImmutableList.of("iterable"); } @Override public Object apply(Object input, Map<String, Object> args) { return input instanceof Iterable ? Iterables.getFirst((Iterable<?>) input, null) : null; } } public class IterableTailFilter implements Filter { @Override public List<String> getArgumentNames() { return ImmutableList.of("iterable"); } @Override public Object apply(Object input, Map<String, Object> args) { return input instanceof Iterable ? Iterables.skip((Iterable<?>) input, 1) : null; } } public class TitleFilter implements Filter { @Override public List<String> getArgumentNames() { return null; } @Override public Object apply(Object input, Map<String, Object> args) { if (input != null && input instanceof String) { return capitalizeFully((String) input); } else { return ""; } } private String capitalizeFully(String str) { char[] chars = str.toLowerCase().toCharArray(); StringBuffer buffer = new StringBuffer(chars.length); boolean capitalizeNext = true; for (char ch : chars) { if (Character.isWhitespace(ch)) { buffer.append(ch); capitalizeNext = true; } else if (capitalizeNext) { buffer.append(Character.toTitleCase(ch)); capitalizeNext = false; } else { buffer.append(ch); } } return buffer.toString(); } } public class ReverseFilter implements Filter { @Override public List<String> getArgumentNames() { return null; } @Override public Object apply(Object input, Map<String, Object> args) { List<Object> list = new ArrayList<Object>(); if (input != null && input instanceof List) { list.addAll((List<?>) input); Collections.reverse(list); } return list; } } private static class I18NFunction implements Function { final List<String> variableArgs = ImmutableList.of("arg0", "arg1", "arg2", "arg3", "arg4", "arg5"); @Override public List<String> getArgumentNames() { return ImmutableList.of("bundle", "key", "arg0", "arg1", "arg2", "arg3", "arg4", "arg5"); } @Override public Object execute(Map<String, Object> args) { String bundle = (String) args.get("bundle"); String key = args.get("key").toString(); return BundleUtil.getString(bundle, key, arguments(args)); } public String[] arguments(Map<String, Object> args) { List<String> values = new ArrayList<>(); for (String variableArg : variableArgs) { if (args.containsKey(variableArg) && args.get(variableArg) instanceof String) { values.add((String) args.get(variableArg)); } } return values.toArray(new String[] {}); } } private static class MapValueFunction implements Function { @Override public List<String> getArgumentNames() { return ImmutableList.of("map", "key"); } @Override public Object execute(Map<String, Object> args) { Object mapObject = args.get("map"); Object keyObject = args.get("key"); Preconditions.checkArgument(mapObject != null && keyObject != null, "Please specify non empty 'map' and 'key'"); Preconditions.checkArgument(mapObject instanceof Map, "The first argument must be of type " + Map.class.getName()); return ((Map<?, ?>) mapObject).get(keyObject); } } public static class RangeFunction implements Function { @Override public List<String> getArgumentNames() { return ImmutableList.of("from", "to"); } @Override public Object execute(Map<String, Object> args) { Range<Integer> range = Range.closedOpen((Integer) args.get("from"), (Integer) args.get("to")); return ContiguousSet.create(range, DiscreteDomain.integers()); } } @Override public Map<String, Filter> getFilters() { Map<String, Filter> map = new HashMap<String, Filter>(); map.put("formatDate", new DateTimeFormaterFilter()); map.put("title", new TitleFilter()); map.put("head", new IterableHeadFilter()); map.put("tail", new IterableTailFilter()); map.put("length", new LengthFilter()); map.put("reverse", new ReverseFilter()); return ImmutableMap.copyOf(map); } public class InTest implements Test { @Override public List<String> getArgumentNames() { return ImmutableList.of("collection"); } @Override public boolean apply(Object input, Map<String, Object> args) { return ((Collection<?>) args.get("collection")).contains(input); } } @Override public Map<String, Test> getTests() { Map<String, Test> tests = new HashMap<>(); tests.put("in", new InTest()); return tests; } @Override public Map<String, Function> getFunctions() { Map<String, Function> functions = new HashMap<>(); functions.put("i18n", new I18NFunction()); functions.put("range", new RangeFunction()); functions.put("entries", new MapEntriesFunction()); functions.put("getValue", new MapValueFunction()); return functions; } @Override public List<TokenParser> getTokenParsers() { return Collections.singletonList(new LazyForTokenParser()); } }