    001/*
    002 * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
    003 *
    004 *  Permission is hereby granted, free of charge, to any person obtaining
    005 *  a copy of this software and associated documentation files (the
    006 *  "Software"), to deal in the Software without restriction, including
    007 *  without limitation the rights to use, copy, modify, merge, publish,
    008 *  distribute, sublicense, and/or sell copies of the Software, and to
    009 *  permit persons to whom the Software is furnished to do so, subject to
    010 *  the following conditions:
    011 *
    012 *  The above copyright notice and this permission notice shall be
    013 *  included in all copies or substantial portions of the Software.
    014 *
    015 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    016 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    017 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    018 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    019 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    020 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    021 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    022 */
    023
    024package co.aikar.commands;
    025
    026
    027import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
    028import org.jetbrains.annotations.Nullable;
    029
    030import java.math.BigDecimal;
    031import java.text.Normalizer;
    032import java.text.Normalizer.Form;
    033import java.text.NumberFormat;
    034import java.util.ArrayList;
    035import java.util.Collection;
    036import java.util.Iterator;
    037import java.util.List;
    038import java.util.Random;
    039import java.util.function.Consumer;
    040import java.util.regex.Matcher;
    041import java.util.regex.Pattern;
    042import java.util.stream.Collectors;
    043import java.util.stream.Stream;
    044
    045@SuppressWarnings({"WeakerAccess", "unused"})
    046public final class ACFUtil {
    047
    048    public static final Random RANDOM = new Random();
    049
    050    private ACFUtil() {
    051    }
    052
    053    public static String padRight(String s, int n) {
    054        return String.format("%1$-" + n + "s", s);
    055    }
    056
    057    public static String padLeft(String s, int n) {
    058        return String.format("%1$" + n + "s", s);
    059    }
    060
    061    public static String formatNumber(Integer balance) {
    062        return NumberFormat.getInstance().format(balance);
    063    }
    064
    065    public static <T extends Enum> T getEnumFromName(T[] types, String name) {
    066        return getEnumFromName(types, name, null);
    067    }
    068
    069    public static <T extends Enum> T getEnumFromName(T[] types, String name, T def) {
    070        for (T type : types) {
    071            if (type.name().equalsIgnoreCase(name)) {
    072                return type;
    073            }
    074        }
    075        return def;
    076    }
    077
    078    public static <T extends Enum> T getEnumFromOrdinal(T[] types, int ordinal) {
    079        for (T type : types) {
    080            if (type.ordinal() == ordinal) {
    081                return type;
    082            }
    083        }
    084        return null;
    085    }
    086
    087    public static String ucfirst(String str) {
    088        return ApacheCommonsLangUtil.capitalizeFully(str);
    089    }
    090
    091    public static Double parseDouble(String var) {
    092        return parseDouble(var, null);
    093    }
    094
    095    public static Double parseDouble(String var, Double def) {
    096        if (var == null) {
    097            return def;
    098        }
    099        try {
    100            return Double.parseDouble(var);
    101        } catch (NumberFormatException ignored) {
    102        }
    103        return def;
    104    }
    105
    106    public static Float parseFloat(String var) {
    107        return parseFloat(var, null);
    108    }
    109
    110    public static Float parseFloat(String var, Float def) {
    111        if (var == null) {
    112            return def;
    113        }
    114        try {
    115            return Float.parseFloat(var);
    116        } catch (NumberFormatException ignored) {
    117        }
    118        return def;
    119    }
    120
    121    public static Long parseLong(String var) {
    122        return parseLong(var, null);
    123    }
    124
    125    public static Long parseLong(String var, Long def) {
    126        if (var == null) {
    127            return def;
    128        }
    129        try {
    130            return Long.parseLong(var);
    131        } catch (NumberFormatException ignored) {
    132        }
    133        return def;
    134    }
    135
    136    public static Integer parseInt(String var) {
    137        return parseInt(var, null);
    138    }
    139
    140    public static Integer parseInt(String var, Integer def) {
    141        if (var == null) {
    142            return def;
    143        }
    144        try {
    145            return Integer.parseInt(var);
    146        } catch (NumberFormatException ignored) {
    147        }
    148        return def;
    149    }
    150
    151    public static boolean randBool() {
    152        return RANDOM.nextBoolean();
    153    }
    154
    155    public static <T> T nullDefault(Object val, Object def) {
    156        //noinspection unchecked
    157        return (T) (val != null ? val : def);
    158    }
    159
    160    public static String join(Collection<String> args) {
    161        return ApacheCommonsLangUtil.join(args, " ");
    162    }
    163
    164    public static String join(Collection<String> args, String sep) {
    165        return ApacheCommonsLangUtil.join(args, sep);
    166    }
    167
    168    public static String join(String[] args) {
    169        return join(args, 0, ' ');
    170    }
    171
    172    public static String join(String[] args, String sep) {
    173        return ApacheCommonsLangUtil.join(args, sep);
    174    }
    175
    176    public static String join(String[] args, char sep) {
    177        return join(args, 0, sep);
    178    }
    179
    180    public static String join(String[] args, int index) {
    181        return join(args, index, ' ');
    182    }
    183
    184    public static String join(String[] args, int index, char sep) {
    185        return ApacheCommonsLangUtil.join(args, sep, index, args.length);
    186    }
    187
    188    public static String simplifyString(String str) {
    189        if (str == null) {
    190            return null;
    191        }
    192        return ACFPatterns.NON_ALPHA_NUMERIC.matcher(str.toLowerCase()).replaceAll("");
    193    }
    194
    195    public static double round(double x, int scale) {
    196        try {
    197            return (new BigDecimal
    198                    (Double.toString(x))
    199                    .setScale(scale, BigDecimal.ROUND_HALF_UP))
    200                    .doubleValue();
    201        } catch (NumberFormatException ex) {
    202            if (Double.isInfinite(x)) {
    203                return x;
    204            } else {
    205                return Double.NaN;
    206            }
    207        }
    208    }
    209
    210    public static int roundUp(int num, int multiple) {
    211        if (multiple == 0) {
    212            return num;
    213        }
    214
    215        int remainder = num % multiple;
    216        if (remainder == 0) {
    217            return num;
    218        }
    219        return num + multiple - remainder;
    220
    221    }
    222
    223    public static String limit(String str, int limit) {
    224        return str.length() > limit ? str.substring(0, limit) : str;
    225    }
    226
    227    /**
    228     * Plain string replacement, escapes replace value.
    229     *
    230     * @param string
    231     * @param pattern
    232     * @param repl
    233     * @return
    234     */
    235    public static String replace(String string, Pattern pattern, String repl) {
    236        return pattern.matcher(string).replaceAll(Matcher.quoteReplacement(repl));
    237    }
    238
    239    /**
    240     * Regex version of {@link #replace(String, Pattern, String)}
    241     *
    242     * @param string
    243     * @param pattern
    244     * @param repl
    245     * @return
    246     */
    247    public static String replacePattern(String string, Pattern pattern, String repl) {
    248        return pattern.matcher(string).replaceAll(repl);
    249    }
    250
    251    /**
    252     * Plain String replacement. If you need regex patterns, see {@link #replacePattern(String, String, String)}
    253     *
    254     * @param string
    255     * @param pattern
    256     * @param repl
    257     * @return
    258     */
    259    public static String replace(String string, String pattern, String repl) {
    260        return replace(string, ACFPatterns.getPattern(Pattern.quote(pattern)), repl);
    261    }
    262
    263    /**
    264     * Regex version of {@link #replace(String, String, String)}
    265     *
    266     * @param string
    267     * @param pattern
    268     * @param repl
    269     * @return
    270     */
    271    public static String replacePattern(String string, String pattern, String repl) {
    272        return replace(string, ACFPatterns.getPattern(pattern), repl);
    273    }
    274
    275    /**
    276     * Pure Regex Pattern matching and replacement, no escaping
    277     *
    278     * @param string
    279     * @param pattern
    280     * @param repl
    281     * @return
    282     */
    283    public static String replacePatternMatch(String string, Pattern pattern, String repl) {
    284        return pattern.matcher(string).replaceAll(repl);
    285    }
    286
    287    /**
    288     * Pure Regex Pattern matching and replacement, no escaping
    289     *
    290     * @param string
    291     * @param pattern
    292     * @param repl
    293     * @return
    294     */
    295    public static String replacePatternMatch(String string, String pattern, String repl) {
    296        return replacePatternMatch(string, ACFPatterns.getPattern(pattern), repl);
    297    }
    298
    299    public static String replaceStrings(String string, String... replacements) {
    300        if (replacements.length < 2 || replacements.length % 2 != 0) {
    301            throw new IllegalArgumentException("Invalid Replacements");
    302        }
    303        for (int i = 0; i < replacements.length; i += 2) {
    304            String key = replacements[i];
    305            String value = replacements[i + 1];
    306            if (value == null) value = "";
    307            string = replace(string, key, value);
    308        }
    309        return string;
    310    }
    311
    312    public static String replacePatterns(String string, String... replacements) {
    313        if (replacements.length < 2 || replacements.length % 2 != 0) {
    314            throw new IllegalArgumentException("Invalid Replacements");
    315        }
    316        for (int i = 0; i < replacements.length; i += 2) {
    317            String key = replacements[i];
    318            String value = replacements[i + 1];
    319            if (value == null) value = "";
    320            string = replacePattern(string, key, value);
    321        }
    322        return string;
    323    }
    324
    325    public static String capitalize(String str, char[] delimiters) {
    326        return ApacheCommonsLangUtil.capitalize(str, delimiters);
    327    }
    328
    329    private static boolean isDelimiter(char ch, char[] delimiters) {
    330        return ApacheCommonsLangUtil.isDelimiter(ch, delimiters);
    331    }
    332
    333    public static <T> T random(List<T> arr) {
    334        if (arr == null || arr.isEmpty()) {
    335            return null;
    336        }
    337        return arr.get(RANDOM.nextInt(arr.size()));
    338    }
    339
    340    public static <T> T random(T[] arr) {
    341        if (arr == null || arr.length == 0) {
    342            return null;
    343        }
    344        return arr[RANDOM.nextInt(arr.length)];
    345    }
    346
    347    /**
    348     * Added as im sure we will try to "Find this" again. This is no different than Enum.values() passed to above method logically
    349     * but the array version is slightly faster.
    350     *
    351     * @param enm
    352     * @param <T>
    353     * @return
    354     */
    355    @Deprecated
    356    public static <T extends Enum<?>> T random(Class<? extends T> enm) {
    357        return random(enm.getEnumConstants());
    358    }
    359
    360    public static String normalize(String s) {
    361        if (s == null) {
    362            return null;
    363        }
    364        return ACFPatterns.NON_PRINTABLE_CHARACTERS.matcher(Normalizer.normalize(s, Form.NFD)).replaceAll("");
    365    }
    366
    367    public static int indexOf(String arg, String[] split) {
    368        for (int i = 0; i < split.length; i++) {
    369            if (arg == null) {
    370                if (split[i] == null) {
    371                    return i;
    372                }
    373            } else if (arg.equals(split[i])) {
    374                return i;
    375            }
    376        }
    377        return -1;
    378    }
    379
    380    public static String capitalizeFirst(String name) {
    381        return capitalizeFirst(name, '_');
    382    }
    383
    384    public static String capitalizeFirst(String name, char separator) {
    385        name = name.toLowerCase();
    386        String[] split = name.split(Character.toString(separator));
    387        StringBuilder total = new StringBuilder(3);
    388        for (String s : split) {
    389            total.append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).append(' ');
    390        }
    391
    392        return total.toString().trim();
    393    }
    394
    395    public static String ltrim(String s) {
    396        int i = 0;
    397        while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
    398            i++;
    399        }
    400        return s.substring(i);
    401    }
    402
    403    public static String rtrim(String s) {
    404        int i = s.length() - 1;
    405        while (i >= 0 && Character.isWhitespace(s.charAt(i))) {
    406            i--;
    407        }
    408        return s.substring(0, i + 1);
    409    }
    410
    411    public static List<String> enumNames(Enum<?>[] values) {
    412        return Stream.of(values).map(Enum::name).collect(Collectors.toList());
    413    }
    414
    415    public static List<String> enumNames(Class<? extends Enum<?>> cls) {
    416        return enumNames(cls.getEnumConstants());
    417    }
    418
    419    public static String combine(String[] args) {
    420        return combine(args, 0);
    421    }
    422
    423    public static String combine(String[] args, int start) {
    424        int size = 0;
    425        for (int i = start; i < args.length; i++) {
    426            size += args[i].length();
    427        }
    428        StringBuilder sb = new StringBuilder(size);
    429        for (int i = start; i < args.length; i++) {
    430            sb.append(args[i]);
    431        }
    432        return sb.toString();
    433    }
    434
    435
    436    @Nullable
    437    public static <E extends Enum<E>> E simpleMatch(Class<? extends Enum<?>> list, String item) {
    438        if (item == null) {
    439            return null;
    440        }
    441        item = ACFUtil.simplifyString(item);
    442        for (Enum<?> s : list.getEnumConstants()) {
    443            String simple = ACFUtil.simplifyString(s.name());
    444            if (item.equals(simple)) {
    445                //noinspection unchecked
    446                return (E) s;
    447            }
    448        }
    449
    450        return null;
    451    }
    452
    453    public static boolean isTruthy(String test) {
    454        switch (test) {
    455            case "t":
    456            case "true":
    457            case "on":
    458            case "y":
    459            case "yes":
    460            case "1":
    461                return true;
    462        }
    463        return false;
    464    }
    465
    466
    467    public static Number parseNumber(String num, boolean suffixes) {
    468        ApplyModifierToNumber applyModifierToNumber = new ApplyModifierToNumber(num, suffixes).invoke();
    469        num = applyModifierToNumber.getNum();
    470        double mod = applyModifierToNumber.getMod();
    471
    472        return Double.parseDouble(num) * mod;
    473    }
    474
    475    public static BigDecimal parseBigNumber(String num, boolean suffixes) {
    476        ApplyModifierToNumber applyModifierToNumber = new ApplyModifierToNumber(num, suffixes).invoke();
    477        num = applyModifierToNumber.getNum();
    478        double mod = applyModifierToNumber.getMod();
    479
    480        BigDecimal big = new BigDecimal(num);
    481        return (mod == 1) ? big : big.multiply(new BigDecimal(mod));
    482    }
    483
    484    public static <T> boolean hasIntersection(Collection<T> list1, Collection<T> list2) {
    485        for (T t : list1) {
    486            if (list2.contains(t)) {
    487                return true;
    488            }
    489        }
    490
    491        return false;
    492    }
    493
    494    public static <T> Collection<T> intersection(Collection<T> list1, Collection<T> list2) {
    495        List<T> list = new ArrayList<>();
    496
    497        for (T t : list1) {
    498            if (list2.contains(t)) {
    499                list.add(t);
    500            }
    501        }
    502
    503        return list;
    504    }
    505
    506    public static int rand(int min, int max) {
    507        return min + RANDOM.nextInt(max - min + 1);
    508    }
    509
    510    /**
    511     * Calculate random between 2 points, excluding a center
    512     * ex: Util.rand(-12, -6, 6, 12) would not return -5 to 5
    513     *
    514     * @param min1
    515     * @param max1
    516     * @param min2
    517     * @param max2
    518     * @return
    519     */
    520    public static int rand(int min1, int max1, int min2, int max2) {
    521        return randBool() ? rand(min1, max1) : rand(min2, max2);
    522    }
    523
    524    public static double rand(double min, double max) {
    525        return RANDOM.nextDouble() * (max - min) + min;
    526    }
    527
    528    public static boolean isNumber(String str) {
    529        return ApacheCommonsLangUtil.isNumeric(str);
    530    }
    531
    532    public static String intToRoman(int integer) {
    533        if (integer == 1) {
    534            return "I";
    535        }
    536        if (integer == 2) {
    537            return "II";
    538        }
    539        if (integer == 3) {
    540            return "III";
    541        }
    542        if (integer == 4) {
    543            return "IV";
    544        }
    545        if (integer == 5) {
    546            return "V";
    547        }
    548        if (integer == 6) {
    549            return "VI";
    550        }
    551        if (integer == 7) {
    552            return "VII";
    553        }
    554        if (integer == 8) {
    555            return "VIII";
    556        }
    557        if (integer == 9) {
    558            return "IX";
    559        }
    560        if (integer == 10) {
    561            return "X";
    562        }
    563        return null;
    564    }
    565
    566    public static boolean isInteger(String string) {
    567        return ACFPatterns.INTEGER.matcher(string).matches();
    568    }
    569
    570    public static boolean isFloat(String string) {
    571        try {
    572            //noinspection ResultOfMethodCallIgnored
    573            Float.parseFloat(string);
    574            return true;
    575        } catch (Exception e) {
    576            return false;
    577        }
    578    }
    579
    580    public static boolean isDouble(String string) {
    581        try {
    582            //noinspection ResultOfMethodCallIgnored
    583            Double.parseDouble(string);
    584            return true;
    585        } catch (Exception e) {
    586            return false;
    587        }
    588    }
    589
    590    public static boolean isBetween(float num, double min, double max) {
    591        return num >= min && num <= max;
    592    }
    593
    594    @SuppressWarnings("SameParameterValue")
    595    public static double precision(double x, int p) {
    596        double pow = Math.pow(10, p);
    597        return Math.round(x * pow) / pow;
    598    }
    599
    600    public static void sneaky(Throwable t) {
    601        //noinspection RedundantTypeArguments
    602        throw ACFUtil.<RuntimeException>superSneaky(t);
    603    }
    604
    605    private static <T extends Throwable> T superSneaky(Throwable t) throws T {
    606        //noinspection ConstantConditions,unchecked
    607        throw (T) t;
    608    }
    609
    610    public static <T> List<T> preformOnImmutable(List<T> list, Consumer<List<T>> action) {
    611        try {
    612            action.accept(list);
    613        } catch (UnsupportedOperationException ex) {
    614            list = new ArrayList<>(list);
    615            action.accept(list);
    616        }
    617
    618        return list;
    619    }
    620
    621    public static <T> T getFirstElement(Iterable<T> iterable) {
    622        if (iterable == null) {
    623            return null;
    624        }
    625        Iterator<T> iterator = iterable.iterator();
    626        if (iterator.hasNext()) {
    627            return iterator.next();
    628        }
    629
    630        return null;
    631    }
    632
    633    private static class ApplyModifierToNumber {
    634        private String num;
    635        private boolean suffixes;
    636        private double mod;
    637
    638        public ApplyModifierToNumber(String num, boolean suffixes) {
    639            this.num = num;
    640            this.suffixes = suffixes;
    641        }
    642
    643        public String getNum() {
    644            return num;
    645        }
    646
    647        public double getMod() {
    648            return mod;
    649        }
    650
    651        public ApplyModifierToNumber invoke() {
    652            mod = 1;
    653            if (suffixes) {
    654                switch (num.charAt(num.length() - 1)) {
    655                    case 'M':
    656                    case 'm':
    657                        mod = 1000000D;
    658                        num = num.substring(0, num.length() - 1);
    659                        break;
    660                    case 'K':
    661                    case 'k':
    662                        mod = 1000D;
    663                        num = num.substring(0, num.length() - 1);
    664                }
    665            }
    666            return this;
    667        }
    668    }
    669}