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