// Copyright 2006-2014 The Apache Software Foundation // // 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.apache.tapestry5.ioc.internal.util; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.tapestry5.ioc.AnnotationProvider; import org.apache.tapestry5.ioc.Locatable; import org.apache.tapestry5.ioc.Location; import org.apache.tapestry5.ioc.Messages; import org.apache.tapestry5.ioc.internal.NullAnnotationProvider; /** * Utility methods class for the Commons package. */ public class InternalCommonsUtils { /** * @since 5.3 */ public final static AnnotationProvider NULL_ANNOTATION_PROVIDER = new NullAnnotationProvider(); private static final Pattern NON_WORD_PATTERN = Pattern.compile("[^\\w]"); /** * Adds a value to a specially organized map where the values are lists of objects. This somewhat simulates a map * that allows multiple values for the same key. * * @param map * to store value into * @param key * for which a value is added * @param value * to add * @param <K> * the type of key * @param <V> * the type of the list */ public static <K, V> void addToMapList(Map<K, List<V>> map, K key, V value) { List<V> list = map.get(key); if (list == null) { list = CollectionFactory.newList(); map.put(key, list); } list.add(value); } /** * Sniffs the object to see if it is a {@link Location} or {@link Locatable}. Returns null if null or not * convertable to a location. */ public static Location locationOf(Object location) { if (location == null) return null; if (location instanceof Location) return (Location) location; if (location instanceof Locatable) return ((Locatable) location).getLocation(); return null; } public static AnnotationProvider toAnnotationProvider(final Method element) { if (element == null) return NULL_ANNOTATION_PROVIDER; return new AnnotationProvider() { @Override public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { return element.getAnnotation(annotationClass); } }; } /** * Used to convert a property expression into a key that can be used to locate various resources (Blocks, messages, * etc.). Strips out any punctuation characters, leaving just words characters (letters, number and the * underscore). * * @param expression a property expression * @return the expression with punctuation removed */ public static String extractIdFromPropertyExpression(String expression) { return replace(expression, NON_WORD_PATTERN, ""); } public static String replace(String input, Pattern pattern, String replacement) { return pattern.matcher(input).replaceAll(replacement); } /** * Looks for a label within the messages based on the id. If found, it is used, otherwise the name is converted to a * user presentable form. */ public static String defaultLabel(String id, Messages messages, String propertyExpression) { String key = id + "-label"; if (messages.contains(key)) return messages.get(key); return toUserPresentable(extractIdFromPropertyExpression(InternalCommonsUtils.lastTerm(propertyExpression))); } /** * Capitalizes the string, and inserts a space before each upper case character (or sequence of upper case * characters). Thus "userId" becomes "User Id", etc. Also, converts underscore into space (and capitalizes the * following word), thus "user_id" also becomes "User Id". */ public static String toUserPresentable(String id) { StringBuilder builder = new StringBuilder(id.length() * 2); char[] chars = id.toCharArray(); boolean postSpace = true; boolean upcaseNext = true; for (char ch : chars) { if (upcaseNext) { builder.append(Character.toUpperCase(ch)); upcaseNext = false; continue; } if (ch == '_') { builder.append(' '); upcaseNext = true; continue; } boolean upperCase = Character.isUpperCase(ch); if (upperCase && !postSpace) builder.append(' '); builder.append(ch); postSpace = upperCase; } return builder.toString(); } /** * @since 5.3 */ public static AnnotationProvider toAnnotationProvider(final Class element) { return new AnnotationProvider() { @Override public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { return annotationClass.cast(element.getAnnotation(annotationClass)); } }; } /** * Pattern used to eliminate leading and trailing underscores and dollar signs. */ static final Pattern NAME_PATTERN = Pattern.compile("^[_|$]*([\\p{javaJavaIdentifierPart}]+?)[_|$]*$", Pattern.CASE_INSENSITIVE); /** * Converts a method to a user presentable string consisting of the containing class name, the method name, and the * short form of the parameter list (the class name of each parameter type, shorn of the package name portion). * * @param method * @return short string representation */ public static String asString(Method method) { StringBuilder buffer = new StringBuilder(); buffer.append(method.getDeclaringClass().getName()); buffer.append('.'); buffer.append(method.getName()); buffer.append('('); for (int i = 0; i < method.getParameterTypes().length; i++) { if (i > 0) buffer.append(", "); String name = method.getParameterTypes()[i].getSimpleName(); buffer.append(name); } return buffer.append(')').toString(); } /** * Strips leading "_" and "$" and trailing "_" from the name. */ public static String stripMemberName(String memberName) { assert InternalCommonsUtils.isNonBlank(memberName); Matcher matcher = NAME_PATTERN.matcher(memberName); if (!matcher.matches()) throw new IllegalArgumentException(String.format("Input '%s' is not a valid Java identifier.", memberName)); return matcher.group(1); } /** * Joins together some number of elements to form a comma separated list. */ public static String join(List elements) { return InternalCommonsUtils.join(elements, ", "); } /** * Joins together some number of elements. If a value in the list is the empty string, it is replaced with the * string "(blank)". * * @param elements * objects to be joined together * @param separator * used between elements when joining */ public static String join(List elements, String separator) { switch (elements.size()) { case 0: return ""; case 1: return String.valueOf(elements.get(0)); default: StringBuilder buffer = new StringBuilder(); boolean first = true; for (Object o : elements) { if (!first) buffer.append(separator); String string = String.valueOf(o); if (string.equals("")) string = "(blank)"; buffer.append(string); first = false; } return buffer.toString(); } } /** * Creates a sorted copy of the provided elements, then turns that into a comma separated list. * * @return the elements converted to strings, sorted, joined with comma ... or "(none)" if the elements are null or * empty */ public static String joinSorted(Collection elements) { if (elements == null || elements.isEmpty()) return "(none)"; List<String> list = CollectionFactory.newList(); for (Object o : elements) list.add(String.valueOf(o)); Collections.sort(list); return join(list); } /** * Returns true if the input is null, or is a zero length string (excluding leading/trailing whitespace). */ public static boolean isBlank(String input) { return input == null || input.length() == 0 || input.trim().length() == 0; } /** * Capitalizes a string, converting the first character to uppercase. */ public static String capitalize(String input) { if (input.length() == 0) return input; return input.substring(0, 1).toUpperCase() + input.substring(1); } public static boolean isNonBlank(String input) { return !isBlank(input); } /** * Return true if the input string contains the marker for symbols that must be expanded. */ public static boolean containsSymbols(String input) { return input.contains("${"); } /** * Searches the string for the final period ('.') character and returns everything after that. The input string is * generally a fully qualified class name, though tapestry-core also uses this method for the occasional property * expression (which is also dot separated). Returns the input string unchanged if it does not contain a period * character. */ public static String lastTerm(String input) { assert isNonBlank(input); int dotx = input.lastIndexOf('.'); if (dotx < 0) return input; return input.substring(dotx + 1); } /** * Extracts the string keys from a map and returns them in sorted order. The keys are converted to strings. * * @param map * the map to extract keys from (may be null) * @return the sorted keys, or the empty set if map is null */ public static List<String> sortedKeys(Map map) { if (map == null) return Collections.emptyList(); List<String> keys = CollectionFactory.newList(); for (Object o : map.keySet()) keys.add(String.valueOf(o)); Collections.sort(keys); return keys; } }