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}