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}