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}