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}