org.sigmah.client.util.ClientUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.sigmah.client.util.ClientUtils.java

Source

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.<br/>
     * 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).<br/>
     *         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.<br/>
     * 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("&lt;pre&gt;my value&lt;/pre&gt;") -> "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;
    }

}