package org.sigmah.client.util; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.sigmah.client.i18n.I18N; import org.sigmah.shared.dto.base.EntityDTO; import org.sigmah.shared.dto.util.EntityConstants; import org.sigmah.shared.util.Pair; import com.extjs.gxt.ui.client.widget.form.SimpleComboBox; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.http.client.URL; import com.google.gwt.http.client.UrlBuilder; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.regexp.shared.MatchResult; import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window.Location; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Widget; /** * Client-side utility methods. * * @author Denis Colliot (dcolliot@ideia.fr) * @author Tom Miette (tmiette@ideia.fr) */ public final class ClientUtils { /** * Provides only static methods. */ private ClientUtils() { // Utility class. } /** * Email client regular expression. */ public static final RegExp EMAIL_CLIENT_REGEXP = RegExp.compile(EntityConstants.EMAIL_REGULAR_EXPRESSION); // ------------------------------------------------------------------------------- // // NAVIGATOR UTILITY METHODS. // // ------------------------------------------------------------------------------- /** * Returns the current user agent. * * @return The current user agent. */ public static String getUserAgent() { return Window.Navigator.getUserAgent(); } /** * The regular expression to extract the IE version from the navigator's app version. */ private static final RegExp ieAppVersionExp = RegExp.compile("MSIE (.+?);"); /** * Returns if the current navigator is a version of Microsoft Internet Explorer. * * @return If the current navigator is a version of Microsoft Internet Explorer. */ public static boolean isIE() { return isIE(null); } /** * Returns if the current navigator is Microsoft Internet Explorer 6. * * @return If the current navigator is Microsoft Internet Explorer 6. */ public static boolean isIE6() { return isIE("6.0"); } /** * Returns if the current navigator is Microsoft Internet Explorer 7. * * @return If the current navigator is Microsoft Internet Explorer 7. */ public static boolean isIE7() { return isIE("7.0"); } /** * Returns if the current navigator is Microsoft Internet Explorer 8. * * @return If the current navigator is Microsoft Internet Explorer 8. */ public static boolean isIE8() { return isIE("8.0"); } /** * Returns if the current navigator is Microsoft Internet Explorer 9. * * @return If the current navigator is Microsoft Internet Explorer 9. */ public static boolean isIE9() { return isIE("9.0"); } /** * Returns if the current navigator is Microsoft Internet Explorer 10. * * @return If the current navigator is Microsoft Internet Explorer 10. */ public static boolean isIE10() { return isIE("10.0"); } /** * Returns if the current navigator is Microsoft Internet Explorer 11. * * @return If the current navigator is Microsoft Internet Explorer 11. */ public static boolean isIE11() { return isIE("11.0"); } /** * Returns if the current navigator is the given version of Microsoft Internet Explorer. * * @param version * The expected version. If the version is <code>null</code>, then this method return is the navigator is one * of the versions of Microsoft Internet Explorer. * @return If the current navigator is the given version of Microsoft Internet Explorer. */ public static boolean isIE(String version) { boolean isIE = "Microsoft Internet Explorer".equals(Window.Navigator.getAppName()); if (isIE && version != null) { final MatchResult result = ieAppVersionExp.exec(Window.Navigator.getAppVersion()); if (result != null) { isIE = version.equals(result.getGroup(1)); } else { isIE = false; } } return isIE; } /** * Returns if the current navigator is Mozilla <b>Firefox</b>. * * @return {@code true} if the current navigator is Firefox. */ public static boolean isFF() { return Window.Navigator.getUserAgent().contains("Firefox"); } /** * Returns if the current navigator is Apple <b>Safari</b>. * * @return {@code true} if the current navigator is Safari. */ public static boolean isSafari() { return Window.Navigator.getUserAgent().contains("Safari"); } /** * Returns if the current operating system is Apple Macintosh. * * @return If the current operating system is Apple Macintosh. */ public static boolean isMac() { final String platform = Window.Navigator.getPlatform(); return platform != null && platform.toUpperCase().contains("MAC"); } /** * Returns if the current operating system is Linux. * * @return If the current operating system is Linux. */ public static boolean isLinux() { final String platform = Window.Navigator.getPlatform(); return platform != null && platform.toUpperCase().contains("LINUX"); } /** * Returns if the current operating system is Microsoft Windows. * * @return If the current operating system is Microsoft Windows. */ public static boolean isWindows() { final String platform = Window.Navigator.getPlatform(); return platform != null && platform.toUpperCase().contains("WIN"); } // ------------------------------------------------------------------------------- // // STRING UTILITY METHODS. // // ------------------------------------------------------------------------------- /** * Returns if the given {@code value} is blank. * * <pre> * isBlank(null) → true * isBlank("") → true * isBlank(" ") → true * isBlank(" test") → false * isBlank(" a ") → false * </pre> * * @param value * The value to test. * @return {@code true} if the given {@code value} is {@code null} or {@code empty}. */ public static boolean isBlank(String value) { return value == null || value.trim().isEmpty(); } /** * Returns if the given {@code value} is not blank. * * <pre> * isNotBlank(null) → false * isNotBlank("") → false * isNotBlank(" ") → false * isNotBlank(" test") → true * isNotBlank(" a ") → true * </pre> * * @param value * The value to test. * @return {@code true} if the given {@code value} is not {@code null} and not {@code empty}. */ public static boolean isNotBlank(String value) { return !isBlank(value); } /** * Trims the given {@code value}. * * @param value * The value to trim. * @return the given {@code value} trimmed or {@code null}. */ public static String trim(String value) { if (value == null) { return null; } return value.trim(); } /** * Trims the given {@code value}, returns {@code empty} if {@code null}. * * @param value * The value to trim. * @return the given {@code value} trimmed or {@code empty} if {@code null}. */ public static String trimToEmpty(String value) { if (isBlank(value)) { return ""; } return value.trim(); } /** * Returns {@code true} if the given {@code value} contains the given {@code pattern}. * * @param value * The value to test. * @param pattern * The pattern to find. * @return {@code true} if the given {@code value} contains the given {@code pattern}. */ public static boolean contains(String value, String pattern) { if (isBlank(value) || isBlank(pattern)) { return false; } return value.contains(pattern); } /** * Returns {@code true} if the given {@code values} contains the given {@code pattern}. * * @param values * The values to test. * @param pattern * The pattern to find. * @return {@code true} if the given {@code values} contains the given {@code pattern}. */ public static boolean contains(String[] values, String pattern) { if (isBlank(pattern) || isEmpty(values)) { return false; } for (final String value : values) { if (value != null && value.trim().equalsIgnoreCase(pattern.trim())) { return true; } } return false; } /** * <p> * Compares the given two values, returning {@code true} if they are equal. * </p> * <p> * {@code null}s are handled without exceptions. Two {@code null} references are considered to be equal. The * comparison is case sensitive. * </p> * * <pre> * equals(null, null) = true * equals(null, "abc") = false * equals("abc", null) = false * equals("abc", "abc") = true * equals("abc", "ABC") = false * </pre> * * @param value1 * The first string value, may be null. * @param value2 * The second string value, may be null. * @return {@code true} if the given values are equal, case sensitive, or both {@code null}. */ public static boolean equals(String value1, String value2) { if (value1 == null && value2 == null) { return true; } return value1 != null && value1.equals(value2); } /** * <p> * Compares the given two values, returning {@code true} if they are equal. * </p> * <p> * {@code null}s are handled without exceptions. Two {@code null} references are considered to be equal. The * comparison is case <b>insensitive</b>. * </p> * * <pre> * equals(null, null) = true * equals(null, "abc") = false * equals("abc", null) = false * equals("abc", "abc") = true * equals("abc", "ABC") = true * </pre> * * @param value1 * The first string value, may be null. * @param value2 * The second string value, may be null. * @return {@code true} if the given values are equal, case <b>insensitive</b>, or both {@code null}. */ public static boolean equalsIgnoreCase(String value1, String value2) { if (value1 == null && value2 == null) { return true; } return value1 != null && value1.equalsIgnoreCase(value2); } /** * <p> * Converts the given value to upper case. * </p> * * <pre> * toUpperCase(null) = null * toUpperCase("") = "" * toUpperCase("aBc") = "ABC" * toUpperCase("ABC") = "ABC" * toUpperCase(" abc ") = "ABC" * </pre> * * @param value * The string value, may be null. * @return the given {@code value} converted to upper case or {@code null}. */ public static String toUpperCase(String value) { if (value == null) { return null; } return value.trim().toUpperCase(); } /** * <p> * Converts the given value to lower case. * </p> * * <pre> * toLowerCase(null) = null * toLowerCase("") = "" * toLowerCase("aBc") = "abc" * toLowerCase("abc") = "abc" * toLowerCase(" ABC ") = "abc" * </pre> * * @param value * The string value, may be null. * @return the given {@code value} converted to lower case or {@code null}. */ public static String toLowerCase(String value) { if (value == null) { return null; } return value.trim().toLowerCase(); } /** * Puts the first text's character to its upper case value. * The rest of the {@code text} is not modified. * * @param text * The initial text. * @return The first character uppered text. */ public static final String upperFirst(String text) { if (isBlank(text)) { return text; } text = text.trim(); return text.substring(0, 1).toUpperCase() + text.substring(1); } /** * <p> * Converts the given {@code name} with proper name format. * </p> * * <pre> * formatName(null) = null * formatName("") = "" * formatName("jean-luc") = "Jean-Luc" * formatName("jean claude") = "Jean Claude" * formatName("bernard") = "Bernard" * formatName("BERNARD") = "Bernard" * formatName(" denis ") = "Denis" * </pre> * * @param name * The name value, may be {@code null}. * @return the given {@code name} formatted or {@code null}. */ public static String formatName(String name) { if (name == null) { return null; } // Space must be the last one ! final Character[] splitters = { '-', ' ' }; name = name.toLowerCase().trim(); for (final char splitter : splitters) { name = formatWords(name.split("[" + splitter + "]"), splitter); } return name; } /** * Formats the given {@code words} and creates a new {@code String} which parts are separated by given * {@code splitChar}. * * @param words * The words. * @param splitChar * The split character used to generate {@code words} array. * @return the generated string. */ private static String formatWords(final String[] words, char splitChar) { if (isEmpty(words)) { return ""; } final StringBuilder builder = new StringBuilder(); for (String word : words) { if (isBlank(word)) { continue; } word = trimToEmpty(word); if ("de".equals(word)) { // Nothing to do here. } else if (word.startsWith("d'")) { word = word.substring(0, 2) + String.valueOf(word.charAt(2)).toUpperCase() + word.substring(3); } else { word = String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1); } builder.append(word).append(splitChar); } if (builder.length() > 0) { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } /** * Replaces the special characters into the given {@code text} by a corresponding value. * <ul> * <li>{@code spaces : $ % ' " \ / * + = - ~ , ; ! ?} are replaced by {@code _}</li> * <li>{@code éèêë€} are replaced by {@code e}</li> * <li>{@code ÈÉÊË} are replaced by {@code E}</li> * <li>{@code àâä@} are replaced by {@code a}</li> * <li>{@code ÀÁÂÄÅ} are replaced by {@code A}</li> * <li>{@code òóôõö} are replaced by {@code o}</li> * <li>{@code ÒÓÔÕÖ} are replaced by {@code O}</li> * <li>{@code îï} are replaced by {@code i}</li> * <li>{@code úùûü} are replaced by {@code u}</li> * <li>{@code ÙÚÛÜ} are replaced by {@code U}</li> * <li>{@code ç} are replaced by {@code c}</li> * <li>{@code Ç} are replaced by {@code C}</li> * </ul> * * @param text * The text. * @return the given {@code text} with special characters replaced. */ public static final String removeSpecialChars(String text) { if (isBlank(text)) { return text; } text = text.replaceAll("[\\s\\:\\$%\'\"\\\\/\\*\\+=\\-|~\\,\\;\\!\\?]", "_"); text = text.replaceAll("[éèêë€]", "e"); text = text.replaceAll("[ÈÉÊË]", "E"); text = text.replaceAll("[àâä@]", "a"); text = text.replaceAll("[ÀÁÂÄÅ]", "A"); text = text.replaceAll("[òóôõö]", "o"); text = text.replaceAll("[ÒÓÔÕÖ]", "O"); text = text.replaceAll("[îï]", "i"); text = text.replaceAll("[úùûü]", "u"); text = text.replaceAll("[ÙÚÛÜ]", "U"); text = text.replaceAll("[ç]", "c"); text = text.replaceAll("[Ç]", "C"); return text; } /** * Gets a CharSequence length or {@code 0} if the CharSequence is {@code null}. * * @param cs * a CharSequence or {@code null} * @return CharSequence length or {@code 0} if the CharSequence is {@code null}. */ public static int length(CharSequence cs) { return cs == null ? 0 : cs.length(); } /** * <p> * Gets a substring from the specified String avoiding exceptions. * </p> * <p> * A negative start position can be used to start/end {@code n} characters from the end of the String. * </p> * <p> * The returned substring starts with the character in the {@code start} position and ends before the {@code end} * position. All position counting is zero-based -- i.e., to start at the beginning of the string use * {@code start = 0}. Negative start and end positions can be used to specify offsets relative to the end of the * String. * </p> * <p> * If {@code start} is not strictly to the left of {@code end}, "" is returned. * </p> * * <pre> * StringUtils.substring(null, *, *) = null * StringUtils.substring("", * , *) = ""; * StringUtils.substring("abc", 0, 2) = "ab" * StringUtils.substring("abc", 2, 0) = "" * StringUtils.substring("abc", 2, 4) = "c" * StringUtils.substring("abc", 4, 6) = "" * StringUtils.substring("abc", 2, 2) = "" * StringUtils.substring("abc", -2, -1) = "b" * StringUtils.substring("abc", -4, 2) = "ab" * </pre> * * @param str * the String to get the substring from, may be null * @param start * the position to start from, negative means count back from the end of the String by this many characters * @param end * the position to end at (exclusive), negative means count back from the end of the String by this many * characters * @return substring from start position to end position, {@code null} if null String input */ public static String substring(final String str, int start, int end) { if (str == null) { return null; } // handle negatives if (end < 0) { end = str.length() + end; // remember end is negative } if (start < 0) { start = str.length() + start; // remember start is negative } // check length next if (end > str.length()) { end = str.length(); } // if start is greater than end, return "" if (start > end) { return ""; } if (start < 0) { start = 0; } if (end < 0) { end = 0; } return str.substring(start, end); } /** * <p> * Capitalizes a String changing the first letter to title case case as per {@link Character#toUpperCase(char)}. No * other letters are changed. * </p> * <p> * A {@code null} input String returns {@code null}. * </p> * * <pre> * StringUtils.capitalize(null) = null * StringUtils.capitalize("") = "" * StringUtils.capitalize("cat") = "Cat" * StringUtils.capitalize("cAt") = "CAt" * </pre> * * @param str * the String to capitalize, may be null * @return the capitalized String, {@code null} if null String input * @see #uncapitalize(String) */ public static String capitalize(final String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return str; } final char firstChar = str.charAt(0); if (Character.isUpperCase(firstChar)) { // already capitalized return str; } return new StringBuilder(strLen).append(Character.toUpperCase(firstChar)).append(str.substring(1)).toString(); } /** * <p> * Uncapitalizes a String changing the first letter to title case as per {@link Character#toLowerCase(char)}. No other * letters are changed. * </p> * <p> * A {@code null} input String returns {@code null}. * </p> * * <pre> * StringUtils.uncapitalize(null) = null * StringUtils.uncapitalize("") = "" * StringUtils.uncapitalize("Cat") = "cat" * StringUtils.uncapitalize("CAT") = "cAT" * </pre> * * @param str * the String to uncapitalize, may be null * @return the uncapitalized String, {@code null} if null String input * @see #capitalize(String) */ public static String uncapitalize(final String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return str; } char firstChar = str.charAt(0); if (Character.isLowerCase(firstChar)) { // already uncapitalized return str; } return new StringBuilder(strLen).append(Character.toLowerCase(firstChar)).append(str.substring(1)).toString(); } /** * Abbreviates the given {@code value} to {@code max} characters (if necessary).<br> * If the {@code value} is abbreviated, the result ends with "{@code ...}". * * <pre> * abbreviate("my value", 150) → "my value" * abbreviate("my value", 5) → "my va..." * abbreviate("my value", 0) → "..." * </pre> * * @param value * The value. * @param max * The max value. * @return The value abbreviated (if necessary). */ public static String abbreviate(final String value, int max) { if (value == null) { return null; } if (max < 0) { max = 0; } return value.length() > max ? value.substring(0, max) + "..." : value; } /** * Adds a colon to the given label. * * @param label * The label. * @return The label with a proper I18N colon. */ public static String colon(String label) { return label != null ? label + I18N.CONSTANTS.form_label_separator() : null; } /** * Removes the last {@code suffix} of the given {@code value}. * * <pre> * removeLastSuffix("a blue dog", null) → "a blue dog" * removeLastSuffix("a blue dog", "") → "a blue dog" * removeLastSuffix(null, *) → null * removeLastSuffix("", *) → "" * removeLastSuffix("a blue dog", "fog") → "a blue dog" * removeLastSuffix("a blue dog", "og") → "a blue d" * removeLastSuffix("a blue dog", "dog") → "a blue " * removeLastSuffix("a blue dog", "a blue dog") → "" * </pre> * * @param value * The value. * @param suffix * The suffix. * @return The given {@code value} without the last {@code suffix}. */ public static String removeLastSuffix(final String value, final String suffix) { if (value == null) { return null; } if (suffix == null || value.length() == 0 || !value.endsWith(suffix)) { return value; } return value.substring(0, value.length() - suffix.length()); } // ------------------------------------------------------------------------------- // // BOOLEAN UTILITY METHODS. // // ------------------------------------------------------------------------------- /** * Returns {@code true} if the given string is a valid <strong>true</strong> boolean value. * * <pre> * isTrue(null) → false * isTrue("true") → true * isTrue("TRUE") → true * isTrue("on") → true * isTrue("ON") → true * isTrue("1") → true * isTrue(" 1 ") → true * </pre> * * @param value * The string value. * @return {@code true} if the given string is a valid <strong>true</strong> boolean value. */ public static boolean isTrue(String value) { return value != null && ("TRUE".equalsIgnoreCase(value.trim()) || "ON".equalsIgnoreCase(value.trim()) || "1".equals(value.trim())); } /** * Returns {@code true} if the given {@link Boolean} value is a {@code true} boolean value. * * <pre> * isTrue(null) → false * isTrue(false) → false * isTrue(true) → true * </pre> * * @param value * The {@link Boolean} value. * @return {@code true} if the given value is a {@code true} boolean value. */ public static boolean isTrue(Boolean value) { return value != null && value.booleanValue(); } /** * Returns {@code false} if the given {@link Boolean} value is a {@code true} boolean value. * * <pre> * isNotTrue(null) → true * isNotTrue(false) → true * isNotTrue(true) → false * </pre> * * @param value * The {@link Boolean} value. * @return {@code false} if the given value is a {@code true} boolean value. */ public static boolean isNotTrue(Boolean value) { return !isTrue(value); } /** * Returns {@code true} if the given {@link Object} value is a {@code true} boolean value. * * <pre> * isTrue(null) → false * isTrue(true) → true * isTrue(false) → true * isTrue(new String("true")) → true * isTrue(new String("TRUE")) → true * isTrue(new String("false")) → false * isTrue(new String("1")) → true * isTrue(new String(" 1 ")) → true * isTrue(new String("ON")) → true * isTrue(new String("on")) → true * isTrue(new String("")) → false * isTrue(new String("abc")) → false * isTrue(new Boolean(true)) → true * isTrue(new Boolean(false)) → false * isTrue(new Integer(0)) → false * isTrue(new Integer(1)) → true * </pre> * * @param value * The {@link Object} value. * @return {@code true} if the given value is a {@code true} boolean value. * @see #isTrue(String) */ public static boolean isTrue(Object value) { return isTrue(String.valueOf(value)); } // ------------------------------------------------------------------------------- // // NUMBER UTILITY METHODS. // // ------------------------------------------------------------------------------- /** * Returns the integer corresponding to the given string {@code value}. * * @param value * The {@link String} value. * @return the integer corresponding to the given string {@code value} or {@code null} if the {@code value} does not * contain a parsable integer. */ public static Integer asInt(String value) { return asInt(value, null); } /** * Returns the integer corresponding to the given string {@code value}. * * @param value * The {@link String} value. * @param defaultValue * The default value returned by the method if the given {@code value} is not a valid integer. * @return the integer corresponding to the given string {@code value} or {@code defaultValue} if the {@code value} * does not contain a parsable integer. */ public static Integer asInt(String value, Integer defaultValue) { try { return Integer.parseInt(value); } catch (NumberFormatException e) { return defaultValue; } } /** * Returns the {@code long} corresponding to the given string {@code value}. * * @param value * The {@link String} value. * @return the integer corresponding to the given string {@code value} or {@code null} if the {@code value} does not * contain a parsable {@code long}. */ public static Long asLong(String value) { return asLong(value, null); } /** * Returns the {@code long} corresponding to the given string {@code value}. * * @param value * The {@link String} value. * @param defaultValue * The default value returned by the method if the given {@code value} is not a valid {@code long}. * @return the integer corresponding to the given string {@code value} or {@code defaultValue} if the {@code value} * does not contain a parsable {@code long}. */ public static Long asLong(String value, Long defaultValue) { try { return Long.parseLong(value); } catch (NumberFormatException e) { return defaultValue; } } /** * Returns the given {@code number} corresponding {@code Integer} value.<br> * If the {@code number} is {@code null}, the method returns {@code null}. * * @param number * The number, may be {@code null}. * @return The given {@code number} corresponding {@code Integer} value, or {@code null}. */ public static Integer getInteger(final Number number) { return number != null ? number.intValue() : null; } /** * Returns the given {@code number} corresponding {@code Long} value.<br> * If the {@code number} is {@code null}, the method returns {@code null}. * * @param number * The number, may be {@code null}. * @return The given {@code number} corresponding {@code Long} value, or {@code null}. */ public static Long getLong(final Number number) { return number != null ? number.longValue() : null; } // ------------------------------------------------------------------------------- // // DATE UTILITY METHODS. // // ------------------------------------------------------------------------------- /** * Returns the given {@code date} corresponding timestamp (in ms).<br> * If the {@code date} is {@code null}, the method returns {@code null}. * * @param date * The date, may be {@code null}. * @return The given {@code date} corresponding timestamp (in ms), or {@code null}. */ public static Long getTimestamp(final Date date) { return date != null ? date.getTime() : null; } // ------------------------------------------------------------------------------- // // COLLECTION UTILITY METHODS. // // ------------------------------------------------------------------------------- /** * Returns if the given {@code collection} is {@code null} or empty. * * @param collection * The collection to test. * @return {@code true} if the given {@code collection} is {@code null} or {@code empty}. */ public static <C extends Collection<?>> boolean isEmpty(C collection) { return collection == null || collection.isEmpty(); } /** * Returns if the given {@code collection} is not {@code null} and not empty. * * @param collection * The collection to test. * @return {@code true} if the given {@code collection} is not {@code null} and not {@code empty}. */ public static <C extends Collection<?>> boolean isNotEmpty(C collection) { return !isEmpty(collection); } /** * Returns if the given {@code array} is {@code null} or empty. * * @param array * The array to test. * @return {@code true} if the given {@code array} is {@code null} or {@code empty}. */ public static <T extends Object> boolean isEmpty(T[] array) { return array == null || array.length == 0; } /** * Returns if the given {@code array} is {@code null} or empty. * * @param array * The array to test. * @return {@code true} if the given {@code array} is {@code null} or {@code empty}. */ public static <T extends Object> boolean isNotEmpty(T[] array) { return !isEmpty(array); } /** * Returns if the given {@code map} is {@code null} or empty. * * @param map * The map to test. * @return {@code true} if the given {@code map} is {@code null} or {@code empty}. */ public static <M extends Map<?, ?>> boolean isEmpty(M map) { return map == null || map.isEmpty(); } /** * Returns if the given {@code map} is not {@code null} and not empty. * * @param map * The map to test. * @return {@code true} if the given {@code map} is not {@code null} and not {@code empty}. */ public static <M extends Map<?, ?>> boolean isNotEmpty(M map) { return !isEmpty(map); } /** * Returns the collection corresponding to the given {@code array}. * * @param array * The array to convert. * @return the collection corresponding to the given {@code array} or {@code null} if the {@code array} is * {@code null}. */ public static <T> List<T> toList(T[] array) { if (array == null) { return null; } return Arrays.asList(array); } /** * Returns the collection corresponding to the given {@code value}. * * @param value * The value to convert. * @return the collection corresponding to the given nullable {@code value}. */ public static <T> List<T> toList(T value) { return Arrays.asList(toArray(value)); } /** * Utility method returning the intersection between the given sets (i.e. elements present among both sets). * * @param firstSet * The first set. * @param secondSet * The second set. * @return The intersection between the given sets (i.e. elements present among both sets). * Never returns {@code null} ; if no intersection, returns empty set. */ public static <T> Set<T> intersect(final Set<T> firstSet, final Set<T> secondSet) { if (isEmpty(firstSet)) { return secondSet == null ? new HashSet<T>(0) : secondSet; } if (isEmpty(secondSet)) { return firstSet; } firstSet.retainAll(secondSet); return firstSet; } /** * Concatenates all the given {@code collections} together (order is conserved). * * <pre> * concat({4,5}, {1,6}, {7,9}) → {4,5,1,6,7,9} * concat({4,5}, null, {7,9}) → {4,5,7,9} * </pre> * * @param collections * The collection(s). * @return The given {@code collections} concatenated together. */ @SafeVarargs public static <T> List<T> concat(final Collection<T>... collections) { final List<T> concatenation = new ArrayList<T>(); for (final Collection<T> collection : collections) { if (collection != null) { concatenation.addAll(collection); } } return concatenation; } // ------------------------------------------------------------------------------- // // WIDGET UTILITY METHODS. // // ------------------------------------------------------------------------------- /** * Sets the given {@code isWidget} display style property (either {@link Display#BLOCK} or {@link Display#NONE}). * * @param isWidget * The widget. * @param visible * {@code true} to set the {@code isWidget} visible by applying the given {@code display} to its style, * {@code false} to hide it. */ public static <W extends IsWidget> void setDisplay(W isWidget, boolean visible) { setDisplay(isWidget, visible ? Display.BLOCK : Display.NONE); } /** * Sets the given {@code isWidget} with the given {@code display} style property. * * @param isWidget * The widget. * @param display * The display type. * @param visible * {@code true} to set the {@code isWidget} visible by applying the given {@code display} to its style, * {@code false} to hide it with {@code Display.NONE}. */ public static <W extends IsWidget> void setDisplay(W isWidget, final Display display, boolean visible) { setDisplay(isWidget, visible ? display : Display.NONE); } /** * Sets the display style property to all given {@link IsWidget}. * * @param display * The display style property. * @param isWidgets * The Widgets. */ public static void setDisplay(final Display display, IsWidget... isWidgets) { if (isEmpty(isWidgets)) { return; } for (final IsWidget widget : isWidgets) { setDisplay(widget, display); } } /** * Sets the given {@code isWidget} display style property. * * @param isWidget * The widget. * @param display * The display style property. Is the given value is {@code null}, the {@code display} style property is * cleared. */ public static <W extends IsWidget> void setDisplay(W isWidget, Display display) { if (isWidget == null) { return; } final Widget widget = isWidget.asWidget(); if (widget == null) { return; } if (display == null) { widget.getElement().getStyle().clearDisplay(); } else { widget.getElement().getStyle().setDisplay(display); } } /** * Adds the given {@code addedStyle} to the given {@code isWidget} and removes the given {@code removedStyles} from * the given {@code isWidget}. * * @param isWidget * The widget. * @param addedStyle * The added style name (if not {@code null} or empty). * @param removedStyles * The removed style names (if not {@code null} or empty). */ public static <W extends IsWidget> void addAndRemoveStyles(W isWidget, String addedStyle, String... removedStyles) { if (isWidget == null) { return; } final Widget widget = isWidget.asWidget(); if (widget == null) { return; } if (isNotEmpty(removedStyles)) { // Removing style names. for (final String removedStyle : removedStyles) { if (isBlank(removedStyle)) { continue; } widget.removeStyleName(removedStyle); } } if (isNotBlank(addedStyle)) { widget.addStyleName(addedStyle); } } /** * Safe access to the given {@code SimpleComboBox} <em>simple</em> value.<br> * Indeed, GXT may throw a {@code ClassCastException} if the value of a {@code SimpleComboBox} is {@code null} and not * of type {@code String}. * * @param field * The {@code SimpleComboBox} field. * @return The simple value or {@code null}. */ public static <T> T getSimpleValue(final SimpleComboBox<T> field) { return field != null && field.getValue() != null ? field.getValue().getValue() : null; } // ------------------------------------------------------------------------------- // // URL UTILITY METHODS. // // ------------------------------------------------------------------------------- /** * Returns the current application URL (with current optional parameters). * * @return the current application URL (with current optional parameters). */ public static String getApplicationUrl() { return getApplicationUrl(true, null); } /** * Returns the current application URL. * * @param withParameters * {@code true} to retrieve existing current URL parameters, {@code false} to forget them. * @return the current application URL (with current optional parameters). */ public static String getApplicationUrl(boolean withParameters) { return getApplicationUrl(withParameters, null); } /** * Returns the current application URL (with current optional parameters) with the given additional parameter * {@code paramName}. * * @param withParameters * {@code true} to retrieve existing current URL parameters, {@code false} to forget them. * @param paramName * The parameter name. * @param paramValues * The parameter value(s). * @return the current application URL (with current optional parameters) with the given additional parameter * {@code paramName}. */ public static String getApplicationUrl(boolean withParameters, String paramName, String... paramValues) { final UrlBuilder urlBuilder = new UrlBuilder(); urlBuilder.setProtocol(Location.getProtocol()); urlBuilder.setHost(Location.getHost()); final Integer port = asInt(Location.getPort()); if (port != null) { urlBuilder.setPort(port); } urlBuilder.setPath(Location.getPath()); if (Location.getParameterMap() != null) { // "?gwt.codesvr=127.0.0.1:9997" for example. for (final Entry<String, List<String>> param : Location.getParameterMap().entrySet()) { if ("gwt.codesvr".equalsIgnoreCase(param.getKey()) && isNotEmpty(param.getValue())) { // Hosted mode parameter exception. urlBuilder.setParameter(param.getKey(), param.getValue().get(0)); } else if (withParameters) { final String[] values = param.getValue() != null ? param.getValue().toArray(new String[param.getValue().size()]) : null; urlBuilder.setParameter(param.getKey(), values); } } } if (isNotBlank(paramName) && paramValues != null) { urlBuilder.setParameter(paramName, paramValues); } return urlBuilder.buildString(); } /** * Appends an additional path to the application URL. * * @param additionalPath * The additional path to append to the application URL. * @return the new URL. */ public static String appendToApplicationUrl(String additionalPath) { final UrlBuilder urlBuilder = new UrlBuilder(); urlBuilder.setProtocol(Location.getProtocol()); urlBuilder.setHost(Location.getHost()); final Integer port = asInt(Location.getPort()); if (port != null) { urlBuilder.setPort(port); } urlBuilder.setPath(Location.getPath() + additionalPath); return urlBuilder.buildString(); } /** * Gets the current date. * * @return The current date. */ public static Date now() { return new Date(); } /** * Formats the given {@code date} using the default pattern {@code dd/MM/yyyy}. * <p> * <b><em>Warning : on <u>server</u> side, the method returns the {@code date} time (in milliseconds).</em></b> * </p> * * @param date * The date to format. * @return the formatted date or {@code null} if the given {@code date} is {@code null}. */ public static String formatDate(final Date date) { return formatDate(date, null); } /** * Formats the given {@code date} using the given pattern {@code pattern}. * <p> * <b><em>Warning : on <u>server</u> side, the method returns the {@code date} time (in milliseconds).</em></b> * </p> * * @param date * The date to format. * @param pattern * The date pattern (default pattern {@code dd/MM/yyyy} is used if {@code null} or empty). * @return the formatted date or {@code null} if the given {@code date} is {@code null}. * @throws IllegalArgumentException * If the given {@code pattern} cannot be parsed. */ public static String formatDate(final Date date, final String pattern) { if (date == null) { return null; } if (GWT.isClient()) { return DateTimeFormat.getFormat(isBlank(pattern) ? "dd/MM/yyyy" : pattern).format(date); } else { // Cannot use 'DateTimeFormat' on server-side. return String.valueOf(date.getTime()); } } /** * Creates a type-safe generic array. * * @param <T> * The array's element type. * @param items * The varargs array items, null allowed. * @return The array, not null unless a null array is passed in * @see org.apache.commons.lang3.ArrayUtils#toArray(Object...) */ @SafeVarargs public static <T> T[] toArray(final T... items) { return items; } /** * Fires a standard window open request from the given url. * * @param url * The URL evaluated by the window open request. */ public static void openWindow(String url) { openWindow(url, false); } /** * Fires a featured or not window open request from the given url with eventual default features. This method's * features are IE10 complient.<br> * <i> Note that Miscrosoft does not support window name as a second parameter of the native {@code Window.open} * method. Can bu used : "_blank", "_media", "_parent", "_search", "_self", "_top".</i>. * * @param url * The URL evaluated by the window open request. * @param featured * If the features must be passed in the method and the zoom parameter appended in url at a 100% fit value. */ public static void openWindow(String url, final boolean featured) { openWindow(url, featured, 0, 0); } /** * Open a window from the given url, sized with the given pixel width and height. This method's features are IE10 * complient.<br> * <i> Note that Miscrosoft does not support window name as a second parameter of the native {@code Window.open} * method. Can bu used : "_blank", "_media", "_parent", "_search", "_self", "_top".</i>. * * @param url * The URL evaluated by the window open request. * @param featured * If the features must be passed in the method and the zoom parameter appended in url at a 100% fit value. * @param widthPx * The window's width feature in pixels (0 or negative value for default).<br> * Should be lower than current screen width. * @param heightPx * The window's height feature in pixels (0 or negative value for default).<br> * Should be lower than current screen height. */ public static void openWindow(String url, final boolean featured, int widthPx, int heightPx) { final int screenWidth = screenWidth(); final int screenHeight = screenHeight(); // Build the eventual features string. final StringBuilder featuresBuilder; if (featured) { featuresBuilder = new StringBuilder(); featuresBuilder.append("resizable=yes,scrollbars=yes,menubar=no,status=no,location=no,titlebar=no,toolbar=no"); if (widthPx > 0) { if (widthPx < screenWidth) { featuresBuilder.append(','); featuresBuilder.append("left="); featuresBuilder.append((screenWidth - widthPx) / 2); } else { widthPx = screenWidth; } featuresBuilder.append(','); featuresBuilder.append("width="); featuresBuilder.append(widthPx); } if (heightPx > 0) { if (heightPx < screenHeight) { featuresBuilder.append(','); featuresBuilder.append("top="); featuresBuilder.append((screenHeight - heightPx) / 2); } else { heightPx = screenHeight; } featuresBuilder.append(','); featuresBuilder.append("height="); featuresBuilder.append(heightPx); } } else { featuresBuilder = new StringBuilder(0); } // Check the URL, eventually configure zoom and cache bypass it. if (!url.contains("rand=") && !url.contains("r=")) { url += (url.indexOf('?') == -1) ? '?' : '&'; url += "rand=" + Math.random(); } if (featured) { url += "#zoom=100"; } // Javascript native call of window opening. // FIXME Check how to prevent IE10 from closing window and suggesting open, save in a bottom popin if featured. Window.open(url, "", featuresBuilder.toString()); } /** * Returns the {@code window.screen.width} property value. * * @return The {@code window.screen.width} property value (in pixels). */ public static native int screenWidth() /*-{ return $wnd.screen.width; }-*/; /** * Returns the {@code window.screen.height} property value. * * @return The {@code window.screen.height} property value (in pixels). */ public static native int screenHeight() /*-{ return $wnd.screen.height; }-*/; /** * Calculates the absolute offset (left, top) of the given {@code element} relatively to root <em>body</em> element. * Automatically includes parent(s) offset(s). * * @param element * The DOM element. * @return The calculated absolute offset (left, top) of the given {@code element} relatively to root <em>body</em> * element. */ public static Pair<Integer, Integer> getAbsoluteOffset(final Element element) { return getAbsoluteOffset(element, 0, 0); } /** * Recursive method used by {@link #getAbsoluteOffset(Element)}. * * @param element * The current DOM element. * @param offsetLeft * The current calculated absolute left offset. * @param offsetTop * The current calculated absolute right offset. * @return The calculated absolute offset (left, top) of an element relatively to root <em>body</em> element. * @see #getAbsoluteOffset(Element) */ private static Pair<Integer, Integer> getAbsoluteOffset(final Element element, final int offsetLeft, final int offsetTop) { if (element == null) { return new Pair<Integer, Integer>(offsetLeft, offsetTop); } else { return getAbsoluteOffset(element.getOffsetParent(), offsetLeft + element.getOffsetLeft(), offsetTop + element.getOffsetTop()); } } /** * Sends the browser a new {@code mailto} request with the given subject and mail addresses. * * @param subject * The mail's subject (can be null). * @param body * The mail's body (can be null). * @param addresses * The mail's addresses collection. */ public static final void mailTo(final String subject, final String body, final Collection<String> addresses) { if (isEmpty(addresses)) { return; } // Filters mails addresses. final List<String> filteredMails = new ArrayList<String>(addresses.size()); for (final String address : addresses) { if (isNotBlank(address) && EMAIL_CLIENT_REGEXP.test(address.trim())) { filteredMails.add(address); } } if (isEmpty(filteredMails)) { return; } Collections.sort(filteredMails); // Builds mailto link. final StringBuilder mailTos = new StringBuilder("mailto:"); for (final String mail : filteredMails) { mailTos.append(mail); mailTos.append(";"); } mailTos.deleteCharAt(mailTos.length() - 1); if (isNotBlank(subject)) { mailTos.append("?subject="); mailTos.append(URL.encodeQueryString(subject)); } if (isNotBlank(body)) { mailTos.append((isNotBlank(subject) ? "&" : "?") + "body="); mailTos.append(URL.encodeQueryString(body)); } Window.Location.assign(mailTos.toString()); } /** * Sends the browser a new {@code mailto} request with the given subject and mail addresses. * * @param subject * The mail's subject (can be null). * @param body * The mail's body (can be null). * @param addresses * The mail's addresses items. */ public static final void mailTo(final String subject, final String body, final String... addresses) { if (isEmpty(addresses)) { return; } mailTo(subject, body, Arrays.asList(addresses)); } /** * Calculates the ratio dimensions for the given arguments. * * @param width * The current width. * @param height * The current height. * @param maxWidth * The max width. * @param maxHeight * The max height. * @return The ratio dimensions (width ; height). */ public static final Pair<Integer, Integer> ratio(final int width, final int height, final int maxWidth, final int maxHeight) { float scale = Math.min(maxWidth * 1.0f / width, maxHeight * 1.0f / height); return new Pair<Integer, Integer>((int) (width * scale), (int) (height * scale)); } /** * Deletes the {@code <pre>} tags from the given {@code value}. * * <pre> * deletePreTags("<pre>my value</pre>") → "my value" * </pre> * * @param value * The value. * @return The value without {@code <pre>} tags. */ public static final String deletePreTags(final String value) { if (isBlank(value)) { return value; } return value.replace("<pre>", "").replace("</pre>", ""); } /** * <p> * Launches the given {@code downloadUrl} corresponding download action (simple {@code GET} access on the URL). * </p> * <p> * In <em>script</em> mode, assigns window location to the given {@code downloadUrl}.<br> * In <em>hosted</em> (dev) mode, opens a new window to avoid loosing the entire JS application context in case of * error. * </p> * * @param downloadUrl * The download URL. * @throws IllegalArgumentException * If the given {@code downloadUrl} is blank. */ public static final void launchDownload(final String downloadUrl) { if (isBlank(downloadUrl)) { throw new IllegalArgumentException("Invalid download URL '" + downloadUrl + "'."); } if (GWT.isScript()) { // Production mode: switch to new page. Window.Location.assign(downloadUrl); } else { // Hosted mode: avoid loosing the entire JS application context in case of error. openWindow(downloadUrl); } } /** * Extracts the {@link Integer} ids of the given {@code entityDTOs}.<br> * If the given {@code entityDTOs} is {@code null}, the method returns {@code null}. * * @param entityDTOs * The collection of {@link EntityDTO} with {@link Integer} id type.<br> * Ignores {@code null} values. * @return The given {@code entityDTOs} corresponding ids. */ public static <E extends EntityDTO<Integer>> List<Integer> getEntityIds(final Collection<E> entityDTOs) { if (entityDTOs == null) { return null; } final List<Integer> ids = new ArrayList<Integer>(); for (final E entity : entityDTOs) { if (entity != null) { ids.add(entity.getId()); } } return ids; } }