001package arcommon; 002 003import java.text.DecimalFormat; 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Comparator; 007import java.util.Iterator; 008import java.util.LinkedHashMap; 009import java.util.List; 010import java.util.regex.Matcher; 011import java.util.regex.Pattern; 012import java.util.regex.PatternSyntaxException; 013 014import arcommon.exception.ExceptionUtil; 015 016public final class StringUtil { 017 public static final int STRING_SPLIT_INCLUDING_TRAILING_EMPTY_STRINGS = -1; 018 019 public static String onlyFirstLinesOf(String string, int maximumNumberOfLines) { 020 int numberOfLines = StringUtil.numberOfCharsInString(string, '\n'); 021 if (numberOfLines > maximumNumberOfLines) { 022 String[] split = string.split("\n"); 023 string = ""; 024 for (int i = 0; i < maximumNumberOfLines; i++) { 025 string += split[i] + "\n"; 026 } 027 string += "... and " + (numberOfLines - maximumNumberOfLines) + " more"; 028 } 029 return string; 030 } 031 032 public static String indentLines(String string, String indent) { 033 String[] split = string.split("\n"); 034 string = ""; 035 for (int i = 0; i < split.length; i++) { 036 string += indent + split[i] + "\n"; 037 } 038 return string; 039 } 040 041 public static int indexOfOccurrence(String baseString, String search, int occurrenceNumber) { 042 if (occurrenceNumber <= 0) { 043 throw new IllegalArgumentException("occurrenceNumber <= 0"); 044 } 045 int fromIndex = 0; 046 int index; 047 do { 048 index = baseString.indexOf(search, fromIndex); 049 if (index == -1) { 050 return -1; 051 } 052 fromIndex = index + 1; 053 occurrenceNumber--; 054 } while (occurrenceNumber > 0); 055 return index; 056 } 057 058 public static String replace(String base, String from, String to) { 059 return replace(base, from, to, true); 060 } 061 062 public static String replace(String base, String from, String to, boolean all) { 063 int index = 0; 064 StringBuilder result = new StringBuilder(base); 065 do { 066 index = result.indexOf(from, index); 067 if (index != -1) { 068 String temp1 = result.substring(0, index); 069 String temp2 = result.substring(index + from.length()); 070 result = new StringBuilder(temp1).append(to).append(temp2); 071 index += to.length(); 072 } 073 } while (index != -1 && all); 074 return result.toString(); 075 } 076 077 private static final char[] FROM = {'\"' ,'\\' ,'\n' ,'\r' ,',' }; 078 private static final String[] TO = {"\\\"", "\\\\", "\\n", "\\r", "\\,"}; 079 080 public static void appendedSeparator(StringBuilder stringBuilder) { 081 if (stringBuilder.length() > 0 && stringBuilder.charAt(stringBuilder.length() - 1) != '\n') { 082 stringBuilder.append(','); 083 } 084 } 085 086 public static void appendedField(StringBuilder stringBuilder, String string) { 087 appendedSeparator(stringBuilder); 088 stringBuilder.append("\""); 089 if (string != null) { 090 wrapString(stringBuilder, string); 091 } 092 stringBuilder.append("\""); 093 } 094 095 public static void wrapString(StringBuilder stringBuilderOutput, String string) { 096 StringBuilder stringBuilder = new StringBuilder(); 097 int offset = 0; 098 while (offset < string.length()) { 099 int lowestPosition = -1; 100 int foundIndex = -1; 101 for (int i = 0; i < FROM.length; i++) { 102 int position = string.indexOf(FROM[i], offset); 103 if (position != -1 && (lowestPosition > position || lowestPosition == -1)) { 104 lowestPosition = position; 105 foundIndex = i; 106 } 107 } 108 if (lowestPosition == -1) { 109 stringBuilder.append(string.substring(offset)); 110 break; 111 } 112 stringBuilder.append(string.substring(offset, lowestPosition)); 113 stringBuilder.append(TO[foundIndex]); 114 offset = lowestPosition + 1; 115 } 116 stringBuilderOutput.append(stringBuilder); 117 } 118 119 public static String unwrapString(String string) { 120 int offset = 0; 121 StringBuilder stringBuilder = new StringBuilder(); 122 while (offset <= string.length()) { 123 int lowestPosition = -1; 124 int foundIndex = -1; 125 for (int i = 0; i < FROM.length; i++) { 126 int position = string.indexOf(TO[i], offset); 127 if (position != -1 && (lowestPosition > position || lowestPosition == -1)) { 128 lowestPosition = position; 129 foundIndex = i; 130 } 131 } 132 if (lowestPosition == -1) { 133 stringBuilder.append(string.substring(offset)); 134 break; 135 } 136 stringBuilder.append(string.substring(offset, lowestPosition)); 137 stringBuilder.append(FROM[foundIndex]); 138 offset = lowestPosition + 2; 139 } 140 141 return stringBuilder.toString(); 142 } 143 144 public static String javaConstantEncodeString(String s) { 145 return javaConstantEncodeString(s, false); 146 } 147 148 public static String javaConstantEncodeString(String s, boolean forceAscii) { 149 StringBuilder stringBuilder = new StringBuilder(); 150 javaConstantEncodeString(stringBuilder, s, forceAscii); 151 return stringBuilder.toString(); 152 } 153 154 public static void javaConstantEncodeString(StringBuilder stringBuilder, String string) { 155 javaConstantEncodeString(stringBuilder, string, false); 156 } 157 158 public static void javaConstantEncodeString(StringBuilder stringBuilder, String string, boolean forceAscii) { 159 for (int i = 0; i < string.length(); i++) { 160 char c = string.charAt(i); 161 switch (c) { 162 // List is from https://codegym.cc/groups/posts/escaping-characters-java 163 case '\t' : 164 stringBuilder.append("\\t"); 165 break; 166 case '\b' : 167 stringBuilder.append("\\b"); 168 break; 169 case '\n' : 170 stringBuilder.append("\\n"); 171 break; 172 case '\r' : 173 stringBuilder.append("\\r"); 174 break; 175 case '\f' : 176 stringBuilder.append("\\f"); 177 break; 178 case '\'' : 179 stringBuilder.append("\\\'"); 180 break; 181 case '\"' : 182 stringBuilder.append("\\\""); 183 break; 184 case '\\' : 185 stringBuilder.append("\\\\"); 186 break; 187 default : 188 if (c < 32 || (forceAscii && c >= 128)) { 189 stringBuilder.append("\\u"); 190 // Code copied from https://stackoverflow.com/questions/6230190/convert-international-string-to-u-codes-in-java 191 // and modified a lot 192 stringBuilder.append(String.format("%04X", (int)c)); 193 } else { 194 stringBuilder.append(c); 195 } 196 break; 197 } 198 } 199 } 200 201 public static String javaConstantDecodeString(String s) { 202 StringBuilder stringBuilder = new StringBuilder(); 203 javaConstantDecodeString(stringBuilder, s); 204 return stringBuilder.toString(); 205 } 206 207 public static void javaConstantDecodeString(StringBuilder stringBuilder, String string) { 208 if (!string.contains("\\")) { 209 stringBuilder.append(string); 210 return ; 211 } 212 213 for (int i = 0; i < string.length(); i++) { 214 char c = string.charAt(i); 215 if (c == '\\') { 216 c = string.charAt(++i); 217 switch (c) { 218 case 't' : 219 stringBuilder.append("\t"); 220 break; 221 case 'b' : 222 stringBuilder.append("\b"); 223 break; 224 case 'n' : 225 stringBuilder.append("\n"); 226 break; 227 case 'r' : 228 stringBuilder.append("\r"); 229 break; 230 case 'f' : 231 stringBuilder.append("\f"); 232 break; 233 case '\\' : 234 stringBuilder.append("\\"); 235 break; 236 case '\"' : 237 stringBuilder.append("\""); 238 break; 239 case '\'' : 240 stringBuilder.append("'"); 241 break; 242 case 'u' : 243 String u = string.substring(i + 1, i + 5); 244 i += 4; 245 try { 246 int uAsHex = Integer.parseInt(u, 16); 247 stringBuilder.append((char)uAsHex); 248 } catch (NumberFormatException e) { 249 throw new RuntimeException(e); 250 } 251 break; 252 default : 253 int from = Math.max(0, i - 50); 254 int to = Math.min(string.length(), i + 50); 255 throw new RuntimeException("Invalid escape character: " + c + " at index: " + i + " in string.substring(): \"" + string.substring(from, to) + "\""); 256 } 257 } else { 258 stringBuilder.append(c); 259 } 260 } 261 } 262 263 public static void appendedField(StringBuilder stringBuilder, int number) { 264 appendedSeparator(stringBuilder); 265 stringBuilder.append(number); 266 } 267 268 public static void appendedField(StringBuilder stringBuilder, long number) { 269 appendedSeparator(stringBuilder); 270 stringBuilder.append(number); 271 } 272 273 public static void appendedField(StringBuilder stringBuilder, boolean b) { 274 appendedSeparator(stringBuilder); 275 stringBuilder.append(b); 276 } 277 278 public static class Fields { 279 private List<String> fields; 280 private int position; 281 private Fields(List<String> pfields) { 282 fields = pfields; 283 } 284 285 public String readString() { 286 return readString(position++); 287 } 288 289 private String readString(int index) { 290 return fields.get(index); 291 } 292 293 public int readInt() { 294 return stringToInt(readString()); 295 } 296 297 public int readIntNoRemove(int index) { 298 return stringToInt(readString(index)); 299 } 300 301 public boolean readBoolean() { 302 return readString().equals("true"); 303 } 304 } 305 306 public static Fields splitStringAtCommaAsFields(String line) { 307 return splitStringAtCommaAsFields(line, true); 308 } 309 310 public static Fields splitStringAtCommaAsFields(String line, boolean safe) { 311 String[] fieldsArray = splitStringAtComma(line, safe); 312 List<String> fields = Arrays.asList(fieldsArray); 313 return new Fields(fields); 314 } 315 316 private static final int OUTSIDE = 0; 317 private static final int IN_FIELD = 1; 318 private static final int IN_STRING = 2; 319 320 public static String[] splitStringAtComma(String line, boolean safe) { 321 ArrayList<String> fields = new ArrayList<String>(40); 322 int mode = OUTSIDE; 323 int fieldStartPosition = 0; 324 boolean backslash = false; 325 StringBuilder stringBuilder = new StringBuilder(); 326 for (int i = 0; i < line.length(); i++) { 327 char c = line.charAt(i); 328 switch (mode) { 329 case OUTSIDE : 330 if (c == '\"') { 331 mode = IN_STRING; 332 fieldStartPosition = i + 1; 333 } else { 334 fieldStartPosition = i; 335 mode = IN_FIELD; 336 } 337 break; 338 case IN_FIELD : 339 if (c == ',') { 340 StringBuilder s = new StringBuilder(line.substring(fieldStartPosition, i)); 341 fields.add(s.toString()); 342 mode = OUTSIDE; 343 } 344 break; 345 case IN_STRING : 346 if (backslash) { 347 backslash = false; 348 switch (c) { 349 case '\"' : 350 stringBuilder.append('\"'); 351 break; 352 case '\\' : 353 stringBuilder.append('\\'); 354 break; 355 case 'n' : 356 stringBuilder.append('\n'); 357 break; 358 case 'r' : 359 stringBuilder.append('\r'); 360 break; 361 case ',' : 362 stringBuilder.append(','); 363 break; 364 } 365 } else { 366 if (c == '\\' && safe) { 367 backslash = true; 368 } else if (c == '\"') { 369 fields.add(stringBuilder.toString()); 370 stringBuilder = new StringBuilder(); 371 i++; 372 mode = OUTSIDE; 373 } else { 374 stringBuilder.append(c); 375 } 376 } 377 break; 378 default : 379 throw new RuntimeException("Internal error"); 380// break; 381 } 382 } 383 if (mode == IN_STRING) { 384 throw new RuntimeException("Internal error"); 385 } 386 if (mode == IN_FIELD) { 387 fields.add(line.substring(fieldStartPosition, line.length())); 388 } 389 return fields.toArray(new String[fields.size()]); 390 } 391 392 public static boolean isStringAnInt(String text) { 393 boolean result = false; 394 try { 395 Integer.parseInt(text); 396 result = true; 397 } catch (Exception e) { 398 result = false; 399 } 400 return result; 401 } 402 403 public static boolean isStringALong(String text) { 404 boolean result = false; 405 try { 406 Long.parseLong(text); 407 result = true; 408 } catch (Exception e) { 409 result = false; 410 } 411 return result; 412 } 413 414 public static boolean isStringADouble(String text) { 415 boolean result = false; 416 try { 417 Double.parseDouble(text); 418 result = true; 419 } catch (Exception e) { 420 result = false; 421 } 422 return result; 423 } 424 425 public static byte stringToByte(String text) { 426 return stringToByte(text, true); 427 } 428 429 public static byte stringToByte(String text, boolean throwOnFail) { 430 byte result = 0; 431 try { 432 result = Byte.parseByte(text); 433 } catch (Exception e) { 434 if (throwOnFail) { 435 throw new RuntimeException("Text: \"" + text + "\"", e); 436 } 437 ExceptionUtil.printException(e); 438 } 439 return result; 440 } 441 442 public static int stringToInt(String text) { 443 return stringToInt(text, true); 444 } 445 446 public static int stringToInt(String text, boolean throwOnFail) { 447 int result = 0; 448 try { 449 result = Integer.parseInt(text); 450 } catch (Exception e) { 451 if (throwOnFail) { 452 throw new RuntimeException("Text: \"" + text + "\"", e); 453 } 454 ExceptionUtil.printException(e); 455 } 456 return result; 457 } 458 459 public static long stringToLong(String text) { 460 return stringToLong(text, true); 461 } 462 463 public static long stringToLong(String text, boolean throwOnFail) { 464 long result = 0; 465 try { 466 result = Long.parseLong(text); 467 } catch (Exception e) { 468 if (throwOnFail) { 469 throw new RuntimeException(text, e); 470 } 471 ExceptionUtil.printException(e); 472 } 473 return result; 474 } 475 476 public static float stringToFloat(String text) { 477 return stringToFloat(text, true); 478 } 479 480 public static float stringToFloat(String text, boolean throwOnFail) { 481 float result = 0.0F; 482 try { 483 text = text.replaceAll(",", "."); 484 result = Float.parseFloat(text); 485 } catch (Exception e) { 486 if (throwOnFail) { 487 throw new RuntimeException(text, e); 488 } 489 ExceptionUtil.printException(e); 490 } 491 return result; 492 } 493 494 public static double stringToDouble(String text) { 495 return stringToDouble(text, true); 496 } 497 498 /** 499 * Return the double value in the text parameter. 500 * <br> 501 * NOTE: This method can't handle strings containing thousands separator(s) 502 */ 503 public static double stringToDouble(String text, boolean throwOnFail) { 504 double result = 0.0; 505 try { 506 text = text.replaceAll(",", "."); 507 result = Double.parseDouble(text); 508 } catch (Exception e) { 509 if (throwOnFail) { 510 throw new RuntimeException(text, e); 511 } 512 ExceptionUtil.printException(e); 513 } 514 return result; 515 } 516 517 public static String longToStringWithFixLengthWith0(long number, int length) { 518 return longToStringWithFixLength(number, length, '0'); 519 } 520 521 public static String longToStringWithFixLengthWithSpace(long number, int length) { 522 return longToStringWithFixLength(number, length, ' '); 523 } 524 525 public static String longToStringWithFixLength(long number, int length, char fillChar) { 526 String result = "" + (number < 0 ? -number : number); 527 if (number < 0) { 528 result = "-" + result; 529 } 530 result = stringWithFixLength(result, length, fillChar); 531 return result; 532 } 533 534 private static DecimalFormat decimalFormat = new DecimalFormat(); 535 536 public static String intToStringWithThousands(int number) { 537 return decimalFormat.format(number); 538 } 539 540 public static String intToStringWithThousands(long number) { 541 return decimalFormat.format(number); 542 } 543 544 private static String[] unit = new String[] {"", "K", "M", "G", "T"}; 545 546 public static String longToStringWithUnit(long number) { 547 boolean negative = number < 0; 548 if (negative) { 549 number = -number; 550 } 551 int log10 = MathUtil.log10(number); 552 if (log10 > 0) { 553 log10--; 554 } 555 log10 = log10 / 3 * 3; 556 double result = number / Math.pow(10, log10); 557 if (result < 10) { 558 result = Math.round(result * 100) / 100.0D; 559 } else { 560 if (result < 100) { 561 result = Math.round(result * 10) / 10.0D; 562 } else { 563 result = Math.round(result); 564 } 565 } 566 int index = log10 / 3; 567 if (index >= unit.length) { 568 return (negative ? "-" : "") + decimalFormat.format(result) + "?"; 569 } 570 return (negative ? "-" : "") + decimalFormat.format(result) + unit[index]; 571 } 572 573 public static int numberOfCharsInString(String baseString, char searchChar) { 574 int result = 0; 575 int fromIndex = 0; 576 do { 577 int index = baseString.indexOf(searchChar, fromIndex); 578 if (index == -1) { 579 break; 580 } 581 fromIndex = index + 1; 582 result++; 583 } while (true); 584 return result; 585 } 586 587 public static String stringWithNumberOfChar(int number, char c) { 588 // String.repeat() is @since 11 so it can't be used here. 589 StringBuilder result = new StringBuilder(); 590 for (int i = 0; i < number; i++) { 591 result.append(c); 592 } 593 return result.toString(); 594 } 595 596 public static String stringWithFixLength(String string, int length, char fillChar) { 597 return stringWithFixLength(string, length, fillChar, STRING_WITH_FIX_LENGTH_ALIGN.ALIGN_RIGHT); 598 } 599 600 public static enum STRING_WITH_FIX_LENGTH_ALIGN {ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT}; 601 602 public static String stringWithFixLength(String string, int length, char fillChar, STRING_WITH_FIX_LENGTH_ALIGN align) { 603 if (length <= 0) { 604 throw new IllegalArgumentException("length <= 0 " + length); 605 } 606 String result = string; 607 if (result.length() < length) { 608 switch (align) { 609 case ALIGN_LEFT : 610 result = result + stringWithNumberOfChar(length - result.length(), fillChar); 611 break; 612 case ALIGN_CENTER : 613 result = stringWithNumberOfChar((length - result.length() + 1) / 2, fillChar) + result + stringWithNumberOfChar((length - result.length()) / 2, fillChar); 614 break; 615 case ALIGN_RIGHT : 616 result = stringWithNumberOfChar(length - result.length(), fillChar) + result; 617 break; 618 default : 619 throw new RuntimeException("Internal error: Unknown align " + align); 620// break; 621 } 622 } 623 return result; 624 } 625 626 private static final long MAXIMUM_LONG_LENGTH = MathUtil.log10(Long.MAX_VALUE); 627 private static Comparator<String> numericStringCompare; 628 629 public static final Comparator<String> getNumericStringCompare() { 630 if (numericStringCompare == null) { 631 numericStringCompare = new Comparator<String>() { 632 public int compare(String string1, String string2) { 633 int position1 = 0; 634 int position2 = 0; 635 int tempPosition1 = 0; 636 int tempPosition2 = 0; 637 int length1 = string1.length(); 638 int length2 = string2.length(); 639 while (position1 < length1 && position2 < length2) { 640 char c1 = string1.charAt(position1); 641 char c2 = string2.charAt(position2); 642 if ((Character.isDigit(c1) || c1 == ' ') && (Character.isDigit(c2) || c1 == ' ')) { 643 tempPosition1 = position1; 644 position1 = readNumberPosition(string1, position1, length1); 645 tempPosition2 = position2; 646 position2 = readNumberPosition(string2, position2, length2); 647 long number1 = readNumberValue(string1, tempPosition1, position1, length1); 648 long number2 = readNumberValue(string2, tempPosition2, position2, length2); 649 if (number1 == Long.MIN_VALUE || number2 == Long.MIN_VALUE) { 650 position1 = tempPosition1; 651 position2 = tempPosition2; 652 } else { 653 if (number1 != number2) { 654 return MathUtil.signum(number1 - number2); 655 } 656 if (position1 >= length1 || position2 >= length2) { 657 break; 658 } 659 c1 = string1.charAt(position1); 660 c2 = string2.charAt(position2); 661 } 662 } 663 if (c1 != c2) { 664 c1 = Character.toUpperCase(c1); 665 c2 = Character.toUpperCase(c2); 666 if (c1 != c2) { 667 c1 = Character.toLowerCase(c1); 668 c2 = Character.toLowerCase(c2); 669 if (c1 != c2) { 670 return c1 - c2; 671 } 672 } 673 } 674 position1++; 675 position2++; 676 } 677 return length1 - length2; 678 } 679 }; 680 } 681 return numericStringCompare; 682 } 683 684 private static int readNumberPosition(String string, int position, int length) { 685 char c; 686 c = string.charAt(position); 687 while (c == ' ') { 688 position++; 689 if (position >= length) { 690 break; 691 } 692 c = string.charAt(position); 693 } 694 while (Character.isDigit(c)) { 695 position++; 696 if (position >= length) { 697 break; 698 } 699 c = string.charAt(position); 700 } 701 return position; 702 } 703 704 private static long readNumberValue(String string, int fromPosition, int toPosition, int length) { 705 String tempString; 706 if (toPosition - fromPosition >= length) { 707 tempString = string.substring(fromPosition); 708 } else { 709 tempString = string.substring(fromPosition, toPosition); 710 } 711 tempString = tempString.trim(); 712 if (tempString.length() == 0) { 713 return Long.MIN_VALUE; 714 } 715 if (tempString.length() >= MAXIMUM_LONG_LENGTH) { 716 return Long.MAX_VALUE; 717 } 718 long number = Long.parseLong(tempString); 719 return number; 720 } 721 722 public static final String DEFAULT_WILDCARD_SEPARATOR = ";"; 723 public static final String DEFAULT_WILDCARD_NOT_PREFIX = "!"; 724 public static final String DEFAULT_WILDCARD_REG_EXP_PREFIX = "~"; 725 726 public static String multiWildcardError(String wildcard) { 727 return multiWildcardError(wildcard, DEFAULT_WILDCARD_SEPARATOR, DEFAULT_WILDCARD_NOT_PREFIX, DEFAULT_WILDCARD_REG_EXP_PREFIX); 728 } 729 730 public static String multiWildcardError(String wildcard, String separator, String notPrefix, String regExpPrefix) { 731 try { 732 doesStringMatchMultiWildcard("Dummy", wildcard, separator, notPrefix, regExpPrefix, false); // Hack 733 return null; 734 } catch (PatternSyntaxException e) { 735 return e.getMessage(); 736 } 737 } 738 739 public static boolean doesStringMatchMultiWildcard(String string, String wildcard, boolean caseSensitive) { 740 return doesStringMatchMultiWildcard(string, wildcard, DEFAULT_WILDCARD_SEPARATOR, DEFAULT_WILDCARD_NOT_PREFIX, DEFAULT_WILDCARD_REG_EXP_PREFIX, caseSensitive); 741 } 742 743 public static boolean doesStringMatchMultiWildcard(String string, String wildcard, String separator, String notPrefix, String regExpPrefix, boolean caseSensitive) { 744 if (wildcard == null || wildcard.length() == 0) { 745 return true; 746 } 747 String[] wildcardSplit = wildcard.split(separator); 748 for (int i = 0; i < wildcardSplit.length; i++) { 749 boolean not = false; 750 if (wildcardSplit[i].startsWith(notPrefix)) { 751 wildcardSplit[i] = wildcardSplit[i].substring(notPrefix.length()); 752 not = true; 753 } 754 if (wildcardSplit[i].startsWith(regExpPrefix)) { 755 String regExp = wildcardSplit[i].substring(regExpPrefix.length()); 756 boolean match = doesStringMatchRegExp(string, regExp); 757 if (match != not) { 758 return true; 759 } 760 } else { 761 if (doesStringMatchWildcard(string, wildcardSplit[i], caseSensitive) != not) { 762 return true; 763 } 764 } 765 } 766 return false; 767 } 768 769 public static final int DEFAULT_REG_EXP_FLAGS = Pattern.DOTALL | Pattern.MULTILINE; 770 771 /** 772 * Returns the index of the first occurrence of regExp in string. Returns -1 if not found. 773 * <br> 774 * "Contains"<br> 775 * For "Equals" see {@link #doesStringMatchRegExp(String, String)} 776 */ 777 public static int indexOfFirstRegExpMatch(String string, String regExp) { 778 return indexOfFirstRegExpMatch(string, regExp, DEFAULT_REG_EXP_FLAGS); 779 } 780 781 public static int indexOfFirstRegExpMatch(String string, String regExp, int flags) { 782 Matcher matcher = getMatcherForRegExp(string, regExp, flags); 783 if (matcher.find()) { 784 return matcher.start(); 785 } 786 return -1; 787 } 788 789 public static String[] getMatchesForRegExp(String string, String regExp) { 790 return getMatchesForRegExp(string, regExp, DEFAULT_REG_EXP_FLAGS); 791 } 792 793 public static String[] getMatchesForRegExp(String string, String regExp, int flags) { 794 Matcher matcher = getMatcherForRegExp(string, regExp, flags); 795 796 ArrayList<String> matches = new ArrayList<String>(); 797 while (matcher.find()) { 798 String match = matcher.group(0); 799 matches.add(match); 800 } 801 802 return matches.toArray(new String[matches.size()]); 803 } 804 805 /** 806 * Returns the result from calling {@link #getFirstGroupForRegExp(String, String, int)} with {@code DEFAULT_REG_EXP_FLAGS}. 807 * <br> 808 * See {@link #getFirstGroupForRegExp(String, String, int)} for details. 809 */ 810 public static String getFirstGroupForRegExp(String string, String regExp) { 811 return getFirstGroupForRegExp(string, regExp, DEFAULT_REG_EXP_FLAGS); 812 } 813 814 /** 815 * Returns the first group of the first match of the regular expression {@code regExp} in {@code string} using {@code flags}. 816 * <br> 817 * If no match or group is found null is returned.<br> 818 * NOTE: If more than one match or group is found an exception is thrown. 819 */ 820 public static String getFirstGroupForRegExp(String string, String regExp, int flags) { 821 String[] groupsForRegExp = getGroupsForRegExpAsFlatArray(string, regExp, DEFAULT_REG_EXP_FLAGS); 822 if (groupsForRegExp.length > 1) { 823 throw new RuntimeException("groupsForRegExp.length > 1 (" + groupsForRegExp.length + ") for string \"" + string + "\", regExp: \"" + regExp + "\" and flags: " + flags); 824 } 825 if (groupsForRegExp.length == 0) { 826 return null; 827 } 828 return groupsForRegExp[0]; 829 } 830 831 public static String[] getGroupsForRegExpAsFlatArrayWithValidation(int expectedLength, String string, String regExp) { 832 return getGroupsForRegExpAsFlatArrayWithValidation(expectedLength, string, regExp, DEFAULT_REG_EXP_FLAGS); 833 } 834 835 public static String[] getGroupsForRegExpAsFlatArrayWithValidation(int expectedLength, String string, String regExp, int flags) { 836 String[] groupsForRegExpAsFlatArray = getGroupsForRegExpAsFlatArray(string, regExp, flags); 837 if (groupsForRegExpAsFlatArray.length != expectedLength) { 838 throw new RuntimeException("groupsForRegExpAsFlatArray.length != " + expectedLength + " (" + groupsForRegExpAsFlatArray.length + " != " + expectedLength + ") for string: \"" + string + "\", regExp: \"" + regExp + "\" and flags: " + flags); 839 } 840 return groupsForRegExpAsFlatArray; 841 } 842 843 public static String[] getGroupsForRegExpAsFlatArray(String string, String regExp) { 844 return getGroupsForRegExpAsFlatArray(string, regExp, DEFAULT_REG_EXP_FLAGS); 845 } 846 847 public static String[] getGroupsForRegExpAsFlatArray(String string, String regExp, int flags) { 848 Matcher matcher = getMatcherForRegExp(string, regExp, flags); 849 850 ArrayList<String> result = new ArrayList<String>(); 851 while (matcher.find()) { 852 int groupCount = matcher.groupCount(); 853 for (int i = 0; i < groupCount; i++) { 854 String group = matcher.group(i + 1); 855 result.add(group); 856 } 857 } 858 859 return result.toArray(new String[result.size()]); 860 } 861 862 public static String[][] getGroupsForRegExpAs2DimensionsArray(String string, String regExp) { 863 return getGroupsForRegExpAs2DimensionsArray(string, regExp, DEFAULT_REG_EXP_FLAGS); 864 } 865 866 public static String[][] getGroupsForRegExpAs2DimensionsArray(String string, String regExp, int flags) { 867 Matcher matcher = getMatcherForRegExp(string, regExp, flags); 868 869 ArrayList<String[]> result = new ArrayList<String[]>(); 870 while (matcher.find()) { 871 int groupCount = matcher.groupCount(); 872 String[] array = new String[groupCount]; 873 result.add(array); 874 for (int i = 0; i < groupCount; i++) { 875 String group = matcher.group(i + 1); 876 array[i] = group; 877 } 878 } 879 880 return result.toArray(new String[result.size()][]); 881 } 882 883 public static String getFirstNamedGroupForRegExp(String string, String regExp, String namedGroup) { 884 return getFirstNamedGroupForRegExp(string, regExp, namedGroup, DEFAULT_REG_EXP_FLAGS); 885 } 886 887 public static String getFirstNamedGroupForRegExp(String string, String regExp, String namedGroup, int flags) { 888 Matcher matcher = StringUtil.getMatcherForRegExp(string, regExp); 889 if (matcher.matches()) { 890 return matcher.group(namedGroup); 891 } 892 return null; 893 } 894 895 /** 896 * Returns true if the entire string matches regular expression in regExp. Otherwise it returns false. 897 * <br> 898 * "Equals"<br> 899 * For "Contains" see {@link #indexOfFirstRegExpMatch(String, String)} 900 */ 901 public static boolean doesStringMatchRegExp(String string, String regExp) { 902 return doesStringMatchRegExp(string, regExp, DEFAULT_REG_EXP_FLAGS); 903 } 904 905 public static boolean doesStringMatchRegExp(String string, String regExp, int flags) { 906 Matcher matcher = getMatcherForRegExp(string, regExp); 907 return matcher.matches(); 908 } 909 910 private static class RegExpFlag { 911 private String regExp; 912 private int flags; 913 914 public RegExpFlag(String pregExp, int pflags) { 915 regExp = pregExp; 916 flags = pflags; 917 } 918 919 @Override 920 public boolean equals(Object obj) { 921 if (!(obj instanceof RegExpFlag)) { 922 throw new RuntimeException("!(obj instanceof RegExpFlag) " + obj.getClass() + " " + obj); 923 } 924 RegExpFlag regExpFlag = (RegExpFlag)obj; 925 return regExpFlag.regExp.equals(regExp) && regExpFlag.flags == flags; 926 } 927 928 @Override 929 public int hashCode() { 930 return regExp.hashCode(); 931 } 932 933 @Override 934 public String toString() { 935 return regExp + ", " + flags; 936 } 937 } 938 939 public static boolean usePatternCache = true; 940 public static int patternCacheSize = 500; 941 private static LinkedHashMap<RegExpFlag, Pattern> regExpCache = new LinkedHashMap<RegExpFlag, Pattern>(); 942 943 public static Matcher getMatcherForRegExp(String string, String regExp) { 944 // See https://www.javamex.com/tutorials/regular_expressions/multiline.shtml 945 return getMatcherForRegExp(string, regExp, DEFAULT_REG_EXP_FLAGS); 946 } 947 948 public static Matcher getMatcherForRegExp(String string, String regExp, int flags) { 949 Pattern pattern; 950 if (usePatternCache) { 951 synchronized (regExpCache) { 952 RegExpFlag regExpFlag = new RegExpFlag(regExp, flags); 953 pattern = regExpCache.get(regExpFlag); 954 if (pattern == null) { 955 pattern = Pattern.compile(regExp, flags); 956 regExpCache.put(regExpFlag, pattern); 957 if (regExpCache.size() > patternCacheSize) { 958 Iterator<RegExpFlag> iterator = regExpCache.keySet().iterator(); 959 iterator.next(); 960 iterator.remove(); 961 } 962 } 963 } 964 } else { 965 pattern = Pattern.compile(regExp, flags); 966 } 967 968 Matcher matcher = pattern.matcher(string); 969 return matcher; 970 } 971 972 public static void clearPatternCache() { 973 synchronized (regExpCache) { 974 regExpCache.clear(); 975 } 976 } 977 978 public static boolean doesStringMatchWildcard(String string, String wildcard, boolean caseSensitive) { 979 if (wildcard.equals("") || wildcard.equals("*")) { 980 return true; 981 } 982 983 if (!caseSensitive) { 984 string = string.toLowerCase(); 985 wildcard = wildcard.toLowerCase(); 986 } 987 988 if (wildcard.indexOf('*') == -1) { 989 return string.equals(wildcard); 990 } 991 992 while (true) { 993 boolean startsWithStar = false; 994 while (wildcard.length() > 0 && wildcard.charAt(0) == '*') { 995 startsWithStar = true; 996 wildcard = wildcard.substring(1); 997 } 998 int position = wildcard.indexOf('*'); 999 String searchString; 1000 if (position == -1) { 1001 searchString = wildcard; 1002 wildcard = ""; 1003 } else { 1004 searchString = wildcard.substring(0, position); 1005 wildcard = wildcard.substring(position); 1006 } 1007 if (wildcard.length() > 0) { 1008 if (startsWithStar) { 1009 position = string.indexOf(searchString); 1010 if (position != -1) { 1011 string = string.substring(position + searchString.length()); 1012 } else { 1013 return false; 1014 } 1015 } else { 1016 if (string.startsWith(searchString)) { 1017 string = string.substring(searchString.length()); 1018 } else { 1019 return false; 1020 } 1021 } 1022 } else { 1023 if (startsWithStar) { 1024 return string.endsWith(searchString); 1025 } 1026 return string.equals(searchString); 1027 } 1028 } 1029 } 1030 1031 public static String byteToBitString(byte number) { 1032 String result = ""; 1033 long numberTemp; 1034 if (number < 0) { 1035 numberTemp = 0xFF + number + 1; 1036 } else { 1037 numberTemp = number; 1038 } 1039 long factor = 1; 1040 do { 1041 result = (numberTemp % (factor * 2)) / factor + result; 1042 factor *= 2; 1043 } while (factor < 256); 1044 return result; 1045 } 1046 1047 public static String intToBitString(int number) { 1048 return intToBitString(number, -1); 1049 } 1050 1051 public static String intToBitString(int number, int length) { 1052 String result = ""; 1053 long factor = 1; 1054 long numberTemp; 1055 if (number < 0) { 1056 numberTemp = 0x7fffffff + number + 1; 1057 } else { 1058 numberTemp = number; 1059 } 1060 if (length > 0) { 1061 for (int i = 0; i < length; i++) { 1062 result = (numberTemp % (factor * 2)) / factor + result; 1063 factor *= 2; 1064 } 1065 } else { 1066 do { 1067 result = (numberTemp % (factor * 2)) / factor + result; 1068 factor *= 2; 1069 } while (numberTemp > factor); 1070 } 1071 return result; 1072 } 1073 1074 public static int bitStringToInt(String bits) { 1075 int result = 0; 1076 for (int i = 0; i < bits.length(); i++) { 1077 if (bits.charAt(i) != '0' && bits.charAt(i) != '1') { 1078 throw new RuntimeException("Wrong char " + bits.charAt(i) + " i " + bits); 1079 } 1080 result = result * 2 + (bits.charAt(i) == '1' ? 1 : 0); 1081 } 1082 return result; 1083 } 1084 1085 public static final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 1086 1087 /** 1088 * Return the number parameter as hex. 1089 * <br> 1090 * @see java.lang.Integer#toString(int, int) see java.lang.Integer#toString(int, 16) 1091 * @see java.lang.Integer#toHexString(int) see java.lang.Integer#toHexString(int) (dynamic length, lower case) 1092 * @see java.lang.Integer#parseInt(String, int) see java.lang.Integer#parseInt("00ff00", 16) 1093 * @see java.lang.String#format(String, Object...) see java.lang.String#format("%04X", Integer) (Always 4 in length, upper case. "%04x" will make it lower case.) 1094 */ 1095 public static String toHex(int number) { 1096 return toHex(number, 0); 1097 } 1098 1099 public static String toHex(int number, int minLength) { 1100 if (number < 0) { 1101 throw new RuntimeException("number < 0"); 1102 } 1103 if (minLength < 0) { 1104 throw new RuntimeException("minLength < 0"); 1105 } 1106 String result = ""; 1107 while (number > 0) { 1108 result = HEX[number % 16] + result; 1109 number /= 16; 1110 } 1111 if (result.length() == 0) { 1112 result = "0"; 1113 } 1114 1115 if (minLength > 0) { 1116 result = StringUtil.stringWithFixLength(result, minLength, '0'); 1117 } 1118 1119 return result; 1120 } 1121 1122 public static char lastChar(String string) { 1123 return string.charAt(string.length() - 1); 1124 } 1125 1126 public static String firstCharUpperCase(String text) { 1127 if (text == null || text.length() == 0) { 1128 return text; 1129 } 1130 return text.substring(0, 1).toUpperCase() + text.substring(1); 1131 } 1132 1133 public static String toWordCase(String text) { 1134 int index = 0; 1135 String result = text; 1136 do { 1137 if (result.length() < index + 1) { 1138 return result; 1139 } 1140 String before = result.substring(0, index); 1141 String wordCaseChar = result.substring(index, index + 1).toUpperCase(); 1142 String after = result.substring(index + 1); 1143 result = before + wordCaseChar + after; 1144 index = result.indexOf(' ', index + 1); 1145 if (index == -1) { 1146 break; 1147 } 1148 index++; 1149 } while (true); 1150 return result; 1151 } 1152 1153 public static String toSentenceCase(String text) { 1154 int index = 0; 1155 String result = text; 1156 do { 1157 String before = result.substring(0, index); 1158 String wordCaseChar = result.substring(index, index + 1).toUpperCase(); 1159 String after = result.substring(index + 1); 1160 result = before + wordCaseChar + after; 1161 index = result.indexOf('.', index + 1); 1162 if (index == -1) { 1163 break; 1164 } 1165 index++; 1166 if (index + 1 >= result.length()) { 1167 return result; 1168 } 1169 1170 while (result.charAt(index) == ' ') { 1171 index++; 1172 if (index + 1 >= result.length()) { 1173 return result; 1174 } 1175 } 1176 } while (true); 1177 return result; 1178 } 1179 1180 public static String toCamelCase(String string) { 1181 String result = ""; 1182 int lastIndex = 0; 1183 do { 1184 int index1 = string.indexOf('_', lastIndex); 1185 int index2 = string.indexOf(' ', lastIndex); 1186 if (index1 == -1 && index2 == -1) { 1187 result += string.substring(lastIndex).toLowerCase(); 1188 break; 1189 } 1190 if (index1 == -1) { 1191 index1 = Integer.MAX_VALUE; 1192 } 1193 if (index2 == -1) { 1194 index2 = Integer.MAX_VALUE; 1195 } 1196 int index = Math.min(index1, index2); 1197 result += string.substring(lastIndex, index).toLowerCase(); 1198 if (index + 1 >= string.length()) { 1199 return result; 1200 } 1201 result += Character.toUpperCase(string.charAt(index + 1)); 1202 lastIndex = index + 2; 1203 } while (true); 1204 return result; 1205 } 1206 1207 public static String toUnderscoreCase(String string) { 1208 boolean lastWasLowerCaseChar = false; 1209 String result = ""; 1210 for (int i = 0; i < string.length(); i++) { 1211 char c = string.charAt(i); 1212 if ((Character.isUpperCase(c) && lastWasLowerCaseChar) || c == ' ') { 1213 result += "_"; 1214 if (c != ' ') { 1215 result += Character.toLowerCase(c); 1216 lastWasLowerCaseChar = true; 1217 } else { 1218 lastWasLowerCaseChar = false; 1219 } 1220 } else { 1221 lastWasLowerCaseChar = Character.isLowerCase(c); 1222 result += c; 1223 } 1224 } 1225 return result; 1226 } 1227 1228 /** 1229 * Returns if s1 and s2 are equal in any combination of null. 1230 */ 1231 public static boolean stringEquals(String s1, String s2) { 1232 if (s1 == null || s2 == null) { 1233 return s1 == s2; 1234 } 1235 return s1.equals(s2); 1236 } 1237}