/* * Copyright 2000-2016 Vaadin Ltd. * * 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 com.vaadin.shared.util; import java.io.Serializable; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Locale; /** * Misc internal utility methods used by both the server and the client package. * * @author Vaadin Ltd * @since 7.1 * */ public class SharedUtil implements Serializable { /** * Checks if a and b are equals using {@link #equals(Object)}. Handles null * values as well. Does not ensure that objects are of the same type. * Assumes that the first object's equals method handle equals properly. * * @param o1 * The first value to compare * @param o2 * The second value to compare * @return true if the objects are equal, false otherwise */ public static boolean equals(Object o1, Object o2) { if (o1 == null) { return o2 == null; } return o1.equals(o2); } /** * Trims trailing slashes (if any) from a string. * * @param value * The string value to be trimmed. Cannot be null. * @return String value without trailing slashes. */ public static String trimTrailingSlashes(String value) { return value.replaceAll("/*$", ""); } /** * RegEx pattern to extract the width/height values. */ public static final String SIZE_PATTERN = "^(-?\\d*(?:\\.\\d+)?)(%|px|em|rem|ex|in|cm|mm|pt|pc)?$"; /** * Splits a camelCaseString into an array of words with the casing * preserved. * * @since 7.4 * @param camelCaseString * The input string in camelCase format * @return An array with one entry per word in the input string */ public static String[] splitCamelCase(String camelCaseString) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < camelCaseString.length(); i++) { char c = camelCaseString.charAt(i); if (Character.isUpperCase(c) && isWordComplete(camelCaseString, i)) { sb.append(' '); } sb.append(c); } return sb.toString().split(" "); } private static boolean isWordComplete(String camelCaseString, int i) { if (i == 0) { // Word can't end at the beginning return false; } else if (!Character.isUpperCase(camelCaseString.charAt(i - 1))) { // Word ends if previous char wasn't upper case return true; } else if (i + 1 < camelCaseString.length() && !Character.isUpperCase(camelCaseString.charAt(i + 1))) { // Word ends if next char isn't upper case return true; } else { return false; } } /** * Converts a camelCaseString to a human friendly format (Camel case * string). * <p> * In general splits words when the casing changes but also handles special * cases such as consecutive upper case characters. Examples: * <p> * {@literal MyBeanContainer} becomes {@literal My Bean Container} * {@literal AwesomeURLFactory} becomes {@literal Awesome URL Factory} * {@literal SomeUriAction} becomes {@literal Some Uri Action} * * @since 7.4 * @param camelCaseString * The input string in camelCase format * @return A human friendly version of the input */ public static String camelCaseToHumanFriendly(String camelCaseString) { String[] parts = splitCamelCase(camelCaseString); for (int i = 0; i < parts.length; i++) { parts[i] = capitalize(parts[i]); } return join(parts, " "); } /** * Converts an UPPER_CASE_STRING to a human friendly format (Upper Case * String). * <p> * Splits words on {@code _}. Examples: * <p> * {@literal MY_BEAN_CONTAINER} becomes {@literal My Bean Container} * {@literal AWESOME_URL_FACTORY} becomes {@literal Awesome Url Factory} * {@literal SOMETHING} becomes {@literal Something} * * @since 7.7.4 * @param upperCaseUnderscoreString * The input string in UPPER_CASE_UNDERSCORE format * @return A human friendly version of the input */ public static String upperCaseUnderscoreToHumanFriendly( String upperCaseUnderscoreString) { String[] parts = upperCaseUnderscoreString.replaceFirst("^_*", "") .split("_"); for (int i = 0; i < parts.length; i++) { parts[i] = capitalize(parts[i].toLowerCase(Locale.ENGLISH)); } return join(parts, " "); } private static boolean isAllUpperCase(String string) { for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); if (!Character.isUpperCase(c) && !Character.isDigit(c)) { return false; } } return true; } /** * Joins the words in the input array together into a single string by * inserting the separator string between each word. * * @since 7.4 * @param parts * The array of words * @param separator * The separator string to use between words * @return The constructed string of words and separators */ public static String join(String[] parts, String separator) { if (parts.length == 0) { return ""; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < parts.length; i++) { sb.append(parts[i]); sb.append(separator); } return sb.substring(0, sb.length() - separator.length()); } /** * Capitalizes the first character in the given string in a way suitable for * use in code (methods, properties etc) * * @since 7.4 * @param string * The string to capitalize * @return The capitalized string */ public static String capitalize(String string) { if (string == null) { return null; } if (string.length() <= 1) { return string.toUpperCase(); } return string.substring(0, 1).toUpperCase(Locale.ENGLISH) + string.substring(1); } /** * Converts a property id to a human friendly format. Handles nested * properties by only considering the last part, e.g. "address.streetName" * is equal to "streetName" for this method. * * @since 7.4 * @param propertyId * The propertyId to format * @return A human friendly version of the property id */ public static String propertyIdToHumanFriendly(Object propertyId) { String string = propertyId.toString(); if (string.isEmpty()) { return ""; } // For nested properties, only use the last part int dotLocation = string.lastIndexOf('.'); if (dotLocation > 0 && dotLocation < string.length() - 1) { string = string.substring(dotLocation + 1); } if (string.matches("^[0-9A-Z_]+$")) { // Deal with UPPER_CASE_PROPERTY_IDS return upperCaseUnderscoreToHumanFriendly(string); } return camelCaseToHumanFriendly(string); } /** * Adds the get parameters to the uri and returns the new uri that contains * the parameters. * * @param uri * The uri to which the parameters should be added. * @param extraParams * One or more parameters in the format "a=b" or "c=d&e=f". An * empty string is allowed but will not modify the url. * @return The modified URI with the get parameters in extraParams added. */ public static String addGetParameters(String uri, String extraParams) { if (extraParams == null || extraParams.length() == 0) { return uri; } // RFC 3986: The query component is indicated by the first question // mark ("?") character and terminated by a number sign ("#") character // or by the end of the URI. String fragment = null; int hashPosition = uri.indexOf('#'); if (hashPosition != -1) { // Fragment including "#" fragment = uri.substring(hashPosition); // The full uri before the fragment uri = uri.substring(0, hashPosition); } if (uri.contains("?")) { uri += "&"; } else { uri += "?"; } uri += extraParams; if (fragment != null) { uri += fragment; } return uri; } /** * Converts a dash ("-") separated string into camelCase. * <p> * Examples: * <p> * {@literal foo} becomes {@literal foo} {@literal foo-bar} becomes * {@literal fooBar} {@literal foo--bar} becomes {@literal fooBar} * * @since 7.5 * @param dashSeparated * The dash separated string to convert * @return a camelCase version of the input string */ public static String dashSeparatedToCamelCase(String dashSeparated) { if (dashSeparated == null) { return null; } String[] parts = dashSeparated.split("-"); for (int i = 1; i < parts.length; i++) { parts[i] = capitalize(parts[i]); } return join(parts, ""); } /** * Checks if the given array contains duplicates (according to * {@link Object#equals(Object)}. * * @param values * the array to check for duplicates * @return <code>true</code> if the array contains duplicates, * <code>false</code> otherwise */ public static boolean containsDuplicates(Object[] values) { int uniqueCount = new HashSet<Object>(Arrays.asList(values)).size(); return uniqueCount != values.length; } /** * Return duplicate values in the given array in the format * "duplicateValue1, duplicateValue2". * * @param values * the values to check for duplicates * @return a comma separated string of duplicates or an empty string if no * duplicates were found */ public static String getDuplicates(Object[] values) { HashSet<Object> set = new HashSet<Object>(); LinkedHashSet<String> duplicates = new LinkedHashSet<String>(); for (Object o : values) { if (!set.add(o)) { duplicates.add(String.valueOf(o)); } } return join(duplicates.toArray(new String[duplicates.size()]), ", "); } }