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