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