001package org.hl7.fhir.utilities; 002 003import static org.apache.commons.lang3.StringUtils.isBlank; 004 005import java.io.BufferedInputStream; 006import java.io.File; 007import java.io.FileInputStream; 008import java.io.FileNotFoundException; 009import java.io.FileOutputStream; 010import java.io.FilenameFilter; 011import java.io.IOException; 012import java.io.InputStream; 013import java.io.UnsupportedEncodingException; 014import java.math.BigDecimal; 015import java.math.RoundingMode; 016import java.net.URLDecoder; 017import java.net.URLEncoder; 018import java.nio.file.Files; 019import java.nio.file.Path; 020import java.nio.file.Paths; 021import java.nio.file.StandardCopyOption; 022import java.time.Duration; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.LinkedList; 028import java.util.List; 029import java.util.Set; 030import java.util.UUID; 031import java.util.zip.ZipEntry; 032import java.util.zip.ZipInputStream; 033 034/* 035 Copyright (c) 2011+, HL7, Inc. 036 All rights reserved. 037 038 Redistribution and use in source and binary forms, with or without modification, 039 are permitted provided that the following conditions are met: 040 041 * Redistributions of source code must retain the above copyright notice, this 042 list of conditions and the following disclaimer. 043 * Redistributions in binary form must reproduce the above copyright notice, 044 this list of conditions and the following disclaimer in the documentation 045 and/or other materials provided with the distribution. 046 * Neither the name of HL7 nor the names of its contributors may be used to 047 endorse or promote products derived from this software without specific 048 prior written permission. 049 050 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 051 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 052 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 053 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 054 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 055 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 056 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 057 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 058 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 059 POSSIBILITY OF SUCH DAMAGE. 060 061 */ 062 063 064import org.apache.commons.io.FileUtils; 065import org.hl7.fhir.exceptions.FHIRException; 066import org.hl7.fhir.utilities.FileNotifier.FileNotifier2; 067 068import javax.annotation.Nullable; 069 070public class Utilities { 071 072 private static final String UUID_REGEX = "[0-9a-f]{8}\\-[0-9a-f]{4}\\-[0-9a-f]{4}\\-[0-9a-f]{4}\\-[0-9a-f]{12}"; 073 private static final String OID_REGEX = "[0-2](\\.(0|[1-9][0-9]*))+"; 074 static final String C_TEMP_DIR = "c:\\temp"; 075 076 /** 077 * Returns the plural form of the word in the string. 078 * <p> 079 * Examples: 080 * 081 * <pre> 082 * inflector.pluralize("post") #=> "posts" 083 * inflector.pluralize("octopus") #=> "octopi" 084 * inflector.pluralize("sheep") #=> "sheep" 085 * inflector.pluralize("words") #=> "words" 086 * inflector.pluralize("the blue mailman") #=> "the blue mailmen" 087 * inflector.pluralize("CamelOctopus") #=> "CamelOctopi" 088 * </pre> 089 * <p> 090 * <p> 091 * <p> 092 * Note that if the {@link Object#toString()} is called on the supplied object, so this method works for non-strings, too. 093 * 094 * @param word the word that is to be pluralized. 095 * @return the pluralized form of the word, or the word itself if it could not be pluralized 096 * @see Inflector#singularize(Object) 097 */ 098 public static String pluralizeMe(String word) { 099 Inflector inf = new Inflector(); 100 return inf.pluralize(word); 101 } 102 103 public static String pluralize(String word, int count) { 104 if (count == 1) 105 return word; 106 Inflector inf = new Inflector(); 107 return inf.pluralize(word); 108 } 109 110 public static boolean isInteger(String string) { 111 if (isBlank(string)) { 112 return false; 113 } 114 String value = string.startsWith("-") ? string.substring(1) : string; 115 for (char next : value.toCharArray()) { 116 if (!Character.isDigit(next)) { 117 return false; 118 } 119 } 120 // check bounds -2,147,483,648..2,147,483,647 121 if (value.length() > 10) 122 return false; 123 if (string.startsWith("-")) { 124 if (value.length() == 10 && string.compareTo("2147483648") > 0) 125 return false; 126 } else { 127 if (value.length() == 10 && string.compareTo("2147483647") > 0) 128 return false; 129 } 130 return true; 131 } 132 133 public static boolean isLong(String string) { 134 if (isBlank(string)) { 135 return false; 136 } 137 String value = string.startsWith("-") ? string.substring(1) : string; 138 for (char next : value.toCharArray()) { 139 if (!Character.isDigit(next)) { 140 return false; 141 } 142 } 143 // check bounds -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807 144 if (value.length() > 20) 145 return false; 146 if (string.startsWith("-")) { 147 if (value.length() == 20 && string.compareTo("9223372036854775808") > 0) 148 return false; 149 } else { 150 if (value.length() == 20 && string.compareTo("9223372036854775807") > 0) 151 return false; 152 } 153 return true; 154 } 155 156 public static boolean isHex(String string) { 157 try { 158 int i = Integer.parseInt(string, 16); 159 return i != i + 1; 160 } catch (Exception e) { 161 return false; 162 } 163 } 164 165 public enum DecimalStatus { 166 BLANK, SYNTAX, RANGE, OK 167 } 168 169 public static boolean isDecimal(String value, boolean allowExponent, boolean allowLeadingZero) { 170 DecimalStatus ds = checkDecimal(value, allowExponent, true); 171 return ds == DecimalStatus.OK || ds == DecimalStatus.RANGE; 172 } 173 174 public static boolean isDecimal(String value, boolean allowExponent) { 175 DecimalStatus ds = checkDecimal(value, allowExponent, false); 176 return ds == DecimalStatus.OK || ds == DecimalStatus.RANGE; 177 } 178 179 public static DecimalStatus checkDecimal(String value, boolean allowExponent, boolean allowLeadingZero) { 180 if (isBlank(value)) { 181 return DecimalStatus.BLANK; 182 } 183 184 // check for leading zeros 185 if (!allowLeadingZero) { 186 if (value.startsWith("0") && !"0".equals(value) && !value.startsWith("0.")) 187 return DecimalStatus.SYNTAX; 188 if (value.startsWith("-0") && !"-0".equals(value) && !value.startsWith("-0.")) 189 return DecimalStatus.SYNTAX; 190 if (value.startsWith("+0") && !"+0".equals(value) && !value.startsWith("+0.")) 191 return DecimalStatus.SYNTAX; 192 } 193 194 // check for trailing dot 195 if (value.endsWith(".")) { 196 return DecimalStatus.SYNTAX; 197 } 198 199 boolean havePeriod = false; 200 boolean haveExponent = false; 201 boolean haveSign = false; 202 boolean haveDigits = false; 203 int preDecLength = 0; 204 int postDecLength = 0; 205 int exponentLength = 0; 206 int length = 0; 207 for (char next : value.toCharArray()) { 208 if (next == '.') { 209 if (!haveDigits || havePeriod || haveExponent) 210 return DecimalStatus.SYNTAX; 211 havePeriod = true; 212 preDecLength = length; 213 length = 0; 214 } else if (next == '-' || next == '+') { 215 if (haveDigits || haveSign) 216 return DecimalStatus.SYNTAX; 217 haveSign = true; 218 } else if (next == 'e' || next == 'E') { 219 if (!haveDigits || haveExponent || !allowExponent) 220 return DecimalStatus.SYNTAX; 221 haveExponent = true; 222 haveSign = false; 223 haveDigits = false; 224 if (havePeriod) 225 postDecLength = length; 226 else 227 preDecLength = length; 228 length = 0; 229 } else if (!Character.isDigit(next)) { 230 return DecimalStatus.SYNTAX; 231 } else { 232 haveDigits = true; 233 length++; 234 } 235 } 236 if (haveExponent && !haveDigits) 237 return DecimalStatus.SYNTAX; 238 if (haveExponent) 239 exponentLength = length; 240 else if (havePeriod) 241 postDecLength = length; 242 else 243 preDecLength = length; 244 245 // now, bounds checking - these are arbitrary 246 if (exponentLength > 4) 247 return DecimalStatus.RANGE; 248 if (preDecLength + postDecLength > 18) 249 return DecimalStatus.RANGE; 250 251 return DecimalStatus.OK; 252 } 253 254 public static String camelCase(String value) { 255 return new Inflector().camelCase(value.trim().replace(" ", "_"), false); 256 } 257 258 public static String upperCamelCase(String value) { 259 return new Inflector().upperCamelCase(value.trim().replace(" ", "_")); 260 } 261 262 public static String escapeXml(String doco) { 263 if (doco == null) 264 return ""; 265 266 StringBuilder b = new StringBuilder(); 267 for (char c : doco.toCharArray()) { 268 if (c == '<') 269 b.append("<"); 270 else if (c == '>') 271 b.append(">"); 272 else if (c == '&') 273 b.append("&"); 274 else if (c == '"') 275 b.append("""); 276 else 277 b.append(c); 278 } 279 return b.toString(); 280 } 281 282 public static String titleize(String s) { 283 StringBuilder b = new StringBuilder(); 284 boolean up = true; 285 for (char c : s.toCharArray()) { 286 if (up) 287 b.append(Character.toUpperCase(c)); 288 else 289 b.append(c); 290 up = c == ' '; 291 } 292 return b.toString(); 293 } 294 295 public static String capitalize(String s) { 296 if (s == null) return null; 297 if (s.length() == 0) return s; 298 if (s.length() == 1) return s.toUpperCase(); 299 300 return s.substring(0, 1).toUpperCase() + s.substring(1); 301 } 302 303 public static void copyDirectory(String sourceFolder, String destFolder, FileNotifier notifier) throws IOException, FHIRException { 304 CSFile src = new CSFile(sourceFolder); 305 if (!src.exists()) 306 throw new FHIRException("Folder " + sourceFolder + " not found"); 307 createDirectory(destFolder); 308 309 String[] files = src.list(); 310 for (String f : files) { 311 if (new CSFile(sourceFolder + File.separator + f).isDirectory()) { 312 if (!f.startsWith(".")) { // ignore .git files... 313 copyDirectory(sourceFolder + File.separator + f, destFolder + File.separator + f, notifier); 314 } 315 } else { 316 if (notifier != null) 317 notifier.copyFile(sourceFolder + File.separator + f, destFolder + File.separator + f); 318 copyFile(new CSFile(sourceFolder + File.separator + f), new CSFile(destFolder + File.separator + f)); 319 } 320 } 321 } 322 323 324 public static void copyDirectory2(String sourceFolder, String destFolder, FileNotifier2 notifier) throws IOException, FHIRException { 325 CSFile src = new CSFile(sourceFolder); 326 if (!src.exists()) 327 throw new FHIRException("Folder " + sourceFolder + " not found"); 328 createDirectory(destFolder); 329 330 String[] files = src.list(); 331 for (String f : files) { 332 if (new CSFile(sourceFolder + File.separator + f).isDirectory()) { 333 if (!f.startsWith(".")) { // ignore .git files... 334 boolean doCopy = notifier != null ? notifier.copyFolder(sourceFolder + File.separator + f, destFolder + File.separator + f) : true; 335 if (doCopy) { 336 copyDirectory2(sourceFolder + File.separator + f, destFolder + File.separator + f, notifier); 337 } 338 } 339 } else { 340 boolean doCopy = notifier != null ? notifier.copyFile(sourceFolder + File.separator + f, destFolder + File.separator + f) : true; 341 if (doCopy) { 342 copyFile(new CSFile(sourceFolder + File.separator + f), new CSFile(destFolder + File.separator + f)); 343 } 344 } 345 } 346 } 347 348 public static void copyFile(String source, String dest) throws IOException { 349 copyFile(new File(source), new File(dest)); 350 } 351 352 public static void copyFile(File sourceFile, File destFile) throws IOException { 353 if (!destFile.exists()) { 354 if (!new CSFile(destFile.getParent()).exists()) { 355 createDirectory(destFile.getParent()); 356 } 357 destFile.createNewFile(); 358 } 359 360 FileInputStream source = null; 361 FileOutputStream destination = null; 362 363 try { 364 source = new FileInputStream(sourceFile); 365 destination = new FileOutputStream(destFile); 366 destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size()); 367 } finally { 368 if (source != null) { 369 source.close(); 370 } 371 if (destination != null) { 372 destination.close(); 373 } 374 } 375 } 376 377 public static boolean checkFolder(String dir, List<String> errors) 378 throws IOException { 379 if (!new CSFile(dir).exists()) { 380 errors.add("Unable to find directory " + dir); 381 return false; 382 } else { 383 return true; 384 } 385 } 386 387 public static boolean checkFile(String purpose, String dir, String file, List<String> errors) 388 throws IOException { 389 if (!new CSFile(dir + file).exists()) { 390 if (errors != null) 391 errors.add("Unable to find " + purpose + " file " + file + " in " + dir); 392 return false; 393 } else { 394 return true; 395 } 396 } 397 398 public static String asCSV(List<String> strings) { 399 StringBuilder s = new StringBuilder(); 400 boolean first = true; 401 for (String n : strings) { 402 if (!first) 403 s.append(","); 404 s.append(n); 405 first = false; 406 } 407 return s.toString(); 408 } 409 410 public static String asHtmlBr(String prefix, List<String> strings) { 411 StringBuilder s = new StringBuilder(); 412 boolean first = true; 413 for (String n : strings) { 414 if (!first) 415 s.append("<br/>"); 416 s.append(prefix); 417 s.append(n); 418 first = false; 419 } 420 return s.toString(); 421 } 422 423 public static void clearDirectory(String folder, String... exemptions) throws IOException { 424 File dir = new File(folder); 425 if (dir.exists()) { 426 if (exemptions.length == 0) 427 FileUtils.cleanDirectory(dir); 428 else { 429 String[] files = new CSFile(folder).list(); 430 if (files != null) { 431 for (String f : files) { 432 if (!existsInList(f, exemptions)) { 433 File fh = new CSFile(folder + File.separatorChar + f); 434 if (fh.isDirectory()) 435 clearDirectory(fh.getAbsolutePath()); 436 fh.delete(); 437 } 438 } 439 } 440 } 441 } 442 } 443 444 public static File createDirectory(String path) throws IOException { 445 new CSFile(path).mkdirs(); 446 return new File(path); 447 } 448 449 public static String changeFileExt(String name, String ext) { 450 if (name.lastIndexOf('.') > -1) 451 return name.substring(0, name.lastIndexOf('.')) + ext; 452 else 453 return name + ext; 454 } 455 456 public static String cleanupTextString(String contents) { 457 if (contents == null || contents.trim().equals("")) 458 return null; 459 else 460 return contents.trim(); 461 } 462 463 464 public static boolean noString(String v) { 465 return v == null || v.equals(""); 466 } 467 468 469 public static void bytesToFile(byte[] content, String filename) throws IOException { 470 FileOutputStream out = new FileOutputStream(filename); 471 out.write(content); 472 out.close(); 473 474 } 475 476 477 public static String appendSlash(String definitions) { 478 return definitions.endsWith(File.separator) ? definitions : definitions + File.separator; 479 } 480 481 public static String appendForwardSlash(String definitions) { 482 if (definitions == null) { 483 return "/"; 484 } 485 return definitions.endsWith("/") ? definitions : definitions + "/"; 486 } 487 488 489 public static String fileTitle(String file) { 490 if (file == null) 491 return null; 492 String s = new File(file).getName(); 493 return s.indexOf(".") == -1 ? s : s.substring(0, s.indexOf(".")); 494 } 495 496 497 public static String systemEol() { 498 return System.getProperty("line.separator"); 499 } 500 501 public static String normaliseEolns(String value) { 502 return value.replace("\r\n", "\r").replace("\n", "\r").replace("\r", "\r\n"); 503 } 504 505 506 public static String unescapeXml(String xml) throws FHIRException { 507 if (xml == null) 508 return null; 509 510 StringBuilder b = new StringBuilder(); 511 int i = 0; 512 while (i < xml.length()) { 513 if (xml.charAt(i) == '&') { 514 StringBuilder e = new StringBuilder(); 515 i++; 516 while (xml.charAt(i) != ';') { 517 e.append(xml.charAt(i)); 518 i++; 519 } 520 if (e.toString().equals("lt")) 521 b.append("<"); 522 else if (e.toString().equals("gt")) 523 b.append(">"); 524 else if (e.toString().equals("amp")) 525 b.append("&"); 526 else if (e.toString().equals("quot")) 527 b.append("\""); 528 else if (e.toString().equals("mu")) 529 b.append((char) 956); 530 else 531 throw new FHIRException("unknown XML entity \"" + e.toString() + "\""); 532 } else 533 b.append(xml.charAt(i)); 534 i++; 535 } 536 return b.toString(); 537 } 538 539 public static String unescapeJson(String json) throws FHIRException { 540 if (json == null) 541 return null; 542 543 StringBuilder b = new StringBuilder(); 544 int i = 0; 545 while (i < json.length()) { 546 if (json.charAt(i) == '\\') { 547 i++; 548 char ch = json.charAt(i); 549 switch (ch) { 550 case '"': 551 b.append('b'); 552 break; 553 case '\\': 554 b.append('\\'); 555 break; 556 case '/': 557 b.append('/'); 558 break; 559 case 'b': 560 b.append('\b'); 561 break; 562 case 'f': 563 b.append('\f'); 564 break; 565 case 'n': 566 b.append('\n'); 567 break; 568 case 'r': 569 b.append('\r'); 570 break; 571 case 't': 572 b.append('\t'); 573 break; 574 case 'u': 575 String hex = json.substring(i + 1, i + 5); 576 b.append((char) Integer.parseInt(hex, 16)); 577 break; 578 default: 579 throw new FHIRException("Unknown JSON escape \\" + ch); 580 } 581 } else 582 b.append(json.charAt(i)); 583 i++; 584 } 585 return b.toString(); 586 } 587 588 589 public static boolean isPlural(String word) { 590 word = word.toLowerCase(); 591 if ("restricts".equals(word) || "contains".equals(word) || "data".equals(word) || "specimen".equals(word) || "replaces".equals(word) || "addresses".equals(word) 592 || "supplementalData".equals(word) || "instantiates".equals(word) || "imports".equals(word)) 593 return false; 594 Inflector inf = new Inflector(); 595 return !inf.singularize(word).equals(word); 596 } 597 598 599 public static String padRight(String src, char c, int len) { 600 StringBuilder s = new StringBuilder(); 601 s.append(src); 602 for (int i = 0; i < len - src.length(); i++) 603 s.append(c); 604 return s.toString(); 605 } 606 607 608 public static String padLeft(String src, char c, int len) { 609 StringBuilder s = new StringBuilder(); 610 for (int i = 0; i < len - src.length(); i++) 611 s.append(c); 612 s.append(src); 613 return s.toString(); 614 } 615 616 617 public static String path(String... args) throws IOException { 618 StringBuilder s = new StringBuilder(); 619 boolean d = false; 620 boolean first = true; 621 for (String arg : args) { 622 if (first && arg == null) 623 continue; 624 first = false; 625 if (!d) 626 d = !noString(arg); 627 else if (!s.toString().endsWith(File.separator)) 628 s.append(File.separator); 629 String a = arg; 630 if (s.length() == 0) { 631 if ("[tmp]".equals(a)) { 632 if (hasCTempDir()) { 633 a = C_TEMP_DIR; 634 } else if (ToolGlobalSettings.hasTempPath()) { 635 a = ToolGlobalSettings.getTempPath(); 636 } else { 637 a = System.getProperty("java.io.tmpdir"); 638 } 639 } else if ("[user]".equals(a)) { 640 a = System.getProperty("user.home"); 641 } else if (a.startsWith("[") && a.endsWith("]")) { 642 String ev = System.getenv(a.replace("[", "").replace("]", "")); 643 if (ev != null) { 644 a = ev; 645 } else { 646 a = "null"; 647 } 648 } 649 } 650 a = a.replace("\\", File.separator); 651 a = a.replace("/", File.separator); 652 if (s.length() > 0 && a.startsWith(File.separator)) 653 a = a.substring(File.separator.length()); 654 655 while (a.startsWith(".." + File.separator)) { 656 if (s.length() == 0) { 657 s = new StringBuilder(Paths.get(".").toAbsolutePath().normalize().toString()); 658 } else { 659 String p = s.toString().substring(0, s.length() - 1); 660 if (!p.contains(File.separator)) { 661 s = new StringBuilder(); 662 } else { 663 s = new StringBuilder(p.substring(0, p.lastIndexOf(File.separator)) + File.separator); 664 } 665 } 666 a = a.substring(3); 667 } 668 if ("..".equals(a)) { 669 int i = s.substring(0, s.length() - 1).lastIndexOf(File.separator); 670 s = new StringBuilder(s.substring(0, i + 1)); 671 } else 672 s.append(a); 673 } 674 return s.toString(); 675 } 676 677 private static boolean hasCTempDir() { 678 if (!System.getProperty("os.name").toLowerCase().contains("win")) { 679 return false; 680 } 681 File tmp = new File(C_TEMP_DIR); 682 return tmp.exists() && tmp.isDirectory() && tmp.canWrite(); 683 } 684 685 public static String pathURL(String... args) { 686 StringBuilder s = new StringBuilder(); 687 boolean d = false; 688 for (String arg : args) { 689 if (arg != null) { 690 if (!d) 691 d = !noString(arg); 692 else if (s.toString() != null && !s.toString().endsWith("/") && !arg.startsWith("/")) 693 s.append("/"); 694 s.append(arg); 695 } 696 } 697 return s.toString(); 698 } 699 700 public static String nmtokenize(String cs) { 701 if (cs == null) 702 return ""; 703 StringBuilder s = new StringBuilder(); 704 for (int i = 0; i < cs.length(); i++) { 705 char c = cs.charAt(i); 706 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_') 707 s.append(c); 708 else if (c != ' ') 709 s.append("." + Integer.toString(c)); 710 } 711 return s.toString(); 712 } 713 714 715 public static boolean isToken(String tail) { 716 if (tail == null || tail.length() == 0) 717 return false; 718 boolean result = isAlphabetic(tail.charAt(0)); 719 for (int i = 1; i < tail.length(); i++) { 720 result = result && (isAlphabetic(tail.charAt(i)) || isDigit(tail.charAt(i)) || (tail.charAt(i) == '_') || (tail.charAt(i) == '[') || (tail.charAt(i) == ']')); 721 } 722 return result; 723 } 724 725 public static boolean isTokenChar(char ch) { 726 return isAlphabetic(ch) || (ch == '_'); 727 } 728 729 public static boolean isDigit(char c) { 730 return (c >= '0') && (c <= '9'); 731 } 732 733 734 public static boolean isAlphabetic(char c) { 735 return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); 736 } 737 738 739 public static String getDirectoryForFile(String filepath) { 740 File f = new File(filepath); 741 return f.getParent(); 742 } 743 744 public static String appendPeriod(String s) { 745 if (Utilities.noString(s)) 746 return s; 747 s = s.trim(); 748 if (s.endsWith(".") || s.endsWith("?")) 749 return s; 750 return s + "."; 751 } 752 753 754 public static String removePeriod(String s) { 755 if (Utilities.noString(s)) 756 return s; 757 if (s.endsWith(".")) 758 return s.substring(0, s.length() - 1); 759 return s; 760 } 761 762 763 public static String stripBOM(String string) { 764 return string.replace("\uFEFF", ""); 765 } 766 767 768 public static String oidTail(String id) { 769 if (id == null || !id.contains(".")) 770 return id; 771 return id.substring(id.lastIndexOf(".") + 1); 772 } 773 774 775 public static String oidRoot(String id) { 776 if (id == null || !id.contains(".")) 777 return id; 778 return id.substring(0, id.indexOf(".")); 779 } 780 781 public static String escapeJava(String doco) { 782 if (doco == null) 783 return ""; 784 785 StringBuilder b = new StringBuilder(); 786 for (char c : doco.toCharArray()) { 787 if (c == '\r') 788 b.append("\\r"); 789 else if (c == '\n') 790 b.append("\\n"); 791 else if (c == '"') 792 b.append("\\\""); 793 else if (c == '\\') 794 b.append("\\\\"); 795 else 796 b.append(c); 797 } 798 return b.toString(); 799 } 800 801 802 public static String[] splitByCamelCase(String name) { 803 List<String> parts = new ArrayList<String>(); 804 StringBuilder b = new StringBuilder(); 805 for (int i = 0; i < name.length(); i++) { 806 if (i > 0 && Character.isUpperCase(name.charAt(i))) { 807 parts.add(b.toString()); 808 b = new StringBuilder(); 809 } 810 b.append(Character.toLowerCase(name.charAt(i))); 811 } 812 parts.add(b.toString()); 813 return parts.toArray(new String[]{}); 814 } 815 816 817 public static String encodeUri(String v) { 818 return v.replace(" ", "%20").replace("?", "%3F").replace("=", "%3D").replace("|", "%7C"); 819 } 820 821 822 public static String normalize(String s) { 823 if (noString(s)) 824 return null; 825 StringBuilder b = new StringBuilder(); 826 boolean isWhitespace = false; 827 for (int i = 0; i < s.length(); i++) { 828 char c = s.charAt(i); 829 if (!Character.isWhitespace(c)) { 830 b.append(Character.toLowerCase(c)); 831 isWhitespace = false; 832 } else if (!isWhitespace) { 833 b.append(' '); 834 isWhitespace = true; 835 } 836 } 837 return b.toString().trim(); 838 } 839 840 public static String normalizeSameCase(String s) { 841 if (noString(s)) 842 return null; 843 StringBuilder b = new StringBuilder(); 844 boolean isWhitespace = false; 845 for (int i = 0; i < s.length(); i++) { 846 char c = s.charAt(i); 847 if (!Character.isWhitespace(c)) { 848 b.append(c); 849 isWhitespace = false; 850 } else if (!isWhitespace) { 851 b.append(' '); 852 isWhitespace = true; 853 } 854 } 855 return b.toString().trim(); 856 } 857 858 859 public static void copyFileToDirectory(File source, File destDir) throws IOException { 860 copyFile(source, new File(path(destDir.getAbsolutePath(), source.getName()))); 861 } 862 863 864 public static boolean isWhitespace(String s) { 865 boolean ok = true; 866 for (int i = 0; i < s.length(); i++) 867 ok = ok && Character.isWhitespace(s.charAt(i)); 868 return ok; 869 870 } 871 872 873 public static String URLEncode(String string) { 874 try { 875 return URLEncoder.encode(string, "UTF-8"); 876 } catch (UnsupportedEncodingException e) { 877 throw new Error(e.getMessage()); 878 } 879 } 880 881 882 public static String URLDecode(String ref) { 883 try { 884 return URLDecoder.decode(ref, "UTF-8"); 885 } catch (UnsupportedEncodingException e) { 886 throw new Error(e.getMessage()); 887 } 888 } 889 890 public static boolean charInSet(char value, char... array) { 891 for (int i : array) 892 if (value == i) 893 return true; 894 return false; 895 } 896 897 898 public static boolean charInRange(char ch, char a, char z) { 899 return ch >= a && ch <= z; 900 } 901 902 public static boolean existsInList(String value, List<String> array) { 903 if (value == null) 904 return false; 905 for (String s : array) 906 if (value.equals(s)) 907 return true; 908 return false; 909 } 910 911 public static boolean existsInList(String value, String... array) { 912 if (value == null) 913 return false; 914 for (String s : array) 915 if (value.equals(s)) 916 return true; 917 return false; 918 } 919 920 public static boolean existsInList(int value, int... array) { 921 for (int i : array) 922 if (value == i) 923 return true; 924 return false; 925 } 926 927 public static boolean existsInListNC(String value, String... array) { 928 for (String s : array) 929 if (value.equalsIgnoreCase(s)) 930 return true; 931 return false; 932 } 933 934 public static String stringJoin(String sep, String... array) { 935 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(sep); 936 for (String s : array) 937 if (!noString(s)) 938 b.append(s); 939 return b.toString(); 940 } 941 942 943 public static String getFileNameForName(String name) { 944 return name.toLowerCase(); 945 } 946 947 public static void deleteTempFiles() throws IOException { 948 File file = createTempFile("test", "test"); 949 String folder = getDirectoryForFile(file.getAbsolutePath()); 950 String[] list = new File(folder).list(new FilenameFilter() { 951 public boolean accept(File dir, String name) { 952 return name.startsWith("ohfu-"); 953 } 954 }); 955 if (list != null) { 956 for (String n : list) { 957 new File(path(folder, n)).delete(); 958 } 959 } 960 } 961 962 public static File createTempFile(String prefix, String suffix) throws IOException { 963 // this allows use to eaily identify all our dtemp files and delete them, since delete on Exit doesn't really work. 964 File file = File.createTempFile("ohfu-" + prefix, suffix); 965 file.deleteOnExit(); 966 return file; 967 } 968 969 970 public static boolean isAsciiChar(char ch) { 971 return ch >= ' ' && ch <= '~'; 972 } 973 974 975 public static String makeUuidLC() { 976 return UUID.randomUUID().toString().toLowerCase(); 977 } 978 979 public static String makeUuidUrn() { 980 return "urn:uuid:" + UUID.randomUUID().toString().toLowerCase(); 981 } 982 983 public static boolean isURL(String s) { 984 boolean ok = s.matches("^http(s{0,1})://[a-zA-Z0-9_/\\-\\.]+\\.([A-Za-z/]{2,5})[a-zA-Z0-9_/\\&\\?\\=\\-\\.\\~\\%]*"); 985 return ok; 986 } 987 988 989 public static String escapeJson(String value) { 990 if (value == null) 991 return ""; 992 993 StringBuilder b = new StringBuilder(); 994 for (char c : value.toCharArray()) { 995 if (c == '\r') 996 b.append("\\r"); 997 else if (c == '\n') 998 b.append("\\n"); 999 else if (c == '\t') 1000 b.append("\\t"); 1001 else if (c == '"') 1002 b.append("\\\""); 1003 else if (c == '\\') 1004 b.append("\\\\"); 1005 else if (((int) c) < 32) 1006 b.append("\\u" + Utilities.padLeft(String.valueOf((int) c), '0', 4)); 1007 else 1008 b.append(c); 1009 } 1010 return b.toString(); 1011 } 1012 1013 public static String humanize(String code) { 1014 StringBuilder b = new StringBuilder(); 1015 boolean lastBreak = true; 1016 for (char c : code.toCharArray()) { 1017 if (Character.isLetter(c)) { 1018 if (lastBreak) 1019 b.append(Character.toUpperCase(c)); 1020 else { 1021 if (Character.isUpperCase(c)) 1022 b.append(" "); 1023 b.append(c); 1024 } 1025 lastBreak = false; 1026 } else { 1027 b.append(" "); 1028 lastBreak = true; 1029 } 1030 } 1031 if (b.length() == 0) 1032 return code; 1033 else 1034 return b.toString(); 1035 } 1036 1037 1038 public static String uncapitalize(String s) { 1039 if (s == null) return null; 1040 if (s.length() == 0) return s; 1041 if (s.length() == 1) return s.toLowerCase(); 1042 1043 return s.substring(0, 1).toLowerCase() + s.substring(1); 1044 } 1045 1046 public static int charCount(String s, char c) { 1047 int res = 0; 1048 for (char ch : s.toCharArray()) 1049 if (ch == c) 1050 res++; 1051 return res; 1052 } 1053 1054 public static boolean isOid(String cc) { 1055 return cc.matches(OID_REGEX); 1056 } 1057 1058 public static boolean equals(String one, String two) { 1059 if (one == null && two == null) 1060 return true; 1061 if (one == null || two == null) 1062 return false; 1063 return one.equals(two); 1064 } 1065 1066 1067 public static void deleteAllFiles(String folder, String type) { 1068 File src = new File(folder); 1069 String[] files = src.list(); 1070 for (String f : files) { 1071 if (new File(folder + File.separator + f).isDirectory()) { 1072 deleteAllFiles(folder + File.separator + f, type); 1073 } else if (f.endsWith(type)) { 1074 new File(folder + File.separator + f).delete(); 1075 } 1076 } 1077 1078 } 1079 1080 public static boolean compareIgnoreWhitespace(File f1, File f2) throws IOException { 1081 InputStream in1 = null; 1082 InputStream in2 = null; 1083 try { 1084 in1 = new BufferedInputStream(new FileInputStream(f1)); 1085 in2 = new BufferedInputStream(new FileInputStream(f2)); 1086 1087 int expectedByte = in1.read(); 1088 while (expectedByte != -1) { 1089 boolean w1 = isWhitespace(expectedByte); 1090 if (w1) 1091 while (isWhitespace(expectedByte)) 1092 expectedByte = in1.read(); 1093 int foundByte = in2.read(); 1094 if (w1) { 1095 if (!isWhitespace(foundByte)) 1096 return false; 1097 while (isWhitespace(foundByte)) 1098 foundByte = in2.read(); 1099 } 1100 if (expectedByte != foundByte) 1101 return false; 1102 expectedByte = in1.read(); 1103 } 1104 if (in2.read() != -1) { 1105 return false; 1106 } 1107 return true; 1108 } finally { 1109 if (in1 != null) { 1110 try { 1111 in1.close(); 1112 } catch (IOException e) { 1113 } 1114 } 1115 if (in2 != null) { 1116 try { 1117 in2.close(); 1118 } catch (IOException e) { 1119 } 1120 } 1121 } 1122 } 1123 1124 private static boolean isWhitespace(int b) { 1125 return b == 9 || b == 10 || b == 13 || b == 32; 1126 } 1127 1128 1129 public static boolean compareIgnoreWhitespace(String fn1, String fn2) throws IOException { 1130 return compareIgnoreWhitespace(new File(fn1), new File(fn2)); 1131 } 1132 1133 1134 public static boolean isAbsoluteUrl(String ref) { 1135 if (ref != null && ref.contains(":")) { 1136 String scheme = ref.substring(0, ref.indexOf(":")); 1137 String details = ref.substring(ref.indexOf(":")+1); 1138 return (existsInList(scheme, "http", "https", "urn") || (isToken(scheme) && scheme.equals(scheme.toLowerCase())) || Utilities.startsWithInList(ref, "urn:iso:", "urn:iso-iec:", "urn:iso-cie:", "urn:iso-astm:", "urn:iso-ieee:", "urn:iec:")) 1139 && details != null && details.length() > 0 && !details.contains(" "); // rfc5141 1140 } 1141 return false; 1142 } 1143 1144 public static boolean isAbsoluteUrlLinkable(String ref) { 1145 if (ref != null && ref.contains(":")) { 1146 String scheme = ref.substring(0, ref.indexOf(":")); 1147 String details = ref.substring(ref.indexOf(":")+1); 1148 return (existsInList(scheme, "http", "https", "ftp")) 1149 && details != null && details.length() > 0 && !details.contains(" "); // rfc5141 1150 } 1151 return false; 1152 } 1153 1154 public static boolean equivalent(String l, String r) { 1155 if (Utilities.noString(l) && Utilities.noString(r)) 1156 return true; 1157 if (Utilities.noString(l) || Utilities.noString(r)) 1158 return false; 1159 return l.toLowerCase().equals(r.toLowerCase()); 1160 } 1161 1162 1163 public static boolean equivalentNumber(String l, String r) { 1164 if (Utilities.noString(l) && Utilities.noString(r)) 1165 return true; 1166 if (Utilities.noString(l) || Utilities.noString(r)) 1167 return false; 1168 if (!Utilities.isDecimal(l, true) || !Utilities.isDecimal(r, true)) 1169 return false; 1170 BigDecimal dl = new BigDecimal(l); 1171 BigDecimal dr = new BigDecimal(r); 1172 if (dl.scale() < dr.scale()) { 1173 dr = dr.setScale(dl.scale(), RoundingMode.HALF_UP); 1174 } else if (dl.scale() > dr.scale()) { 1175 dl = dl.setScale(dr.scale(), RoundingMode.HALF_UP); 1176 } 1177 return dl.equals(dr); 1178 } 1179 1180 public static String getFileExtension(String fn) { 1181 return fn.contains(".") ? fn.substring(fn.lastIndexOf(".") + 1) : ""; 1182 } 1183 1184 1185 public static String unCamelCase(String name) { 1186 StringBuilder b = new StringBuilder(); 1187 boolean first = true; 1188 for (char c : name.toCharArray()) { 1189 if (Character.isUpperCase(c)) { 1190 if (!first) 1191 b.append(" "); 1192 b.append(Character.toLowerCase(c)); 1193 } else 1194 b.append(c); 1195 first = false; 1196 } 1197 return b.toString(); 1198 } 1199 1200 1201 public static boolean isAbsoluteFileName(String source) { 1202 if (isWindows()) 1203 return (source.length() > 2 && source.charAt(1) == ':') || source.startsWith("\\\\"); 1204 else 1205 return source.startsWith("//"); 1206 } 1207 1208 1209 public static boolean isWindows() { 1210 return System.getProperty("os.name").startsWith("Windows"); 1211 } 1212 1213 1214 public static String splitLineForLength(String line, int prefixLength, int indent, int allowedLength) { 1215 List<String> list = new ArrayList<String>(); 1216 while (prefixLength + line.length() > allowedLength) { 1217 int i = allowedLength - (list.size() == 0 ? prefixLength : indent); 1218 while (i > 0 && line.charAt(i) != ' ') 1219 i--; 1220 if (i == 0) 1221 break; 1222 list.add(line.substring(0, i)); 1223 line = line.substring(i + 1); 1224 } 1225 list.add(line); 1226 StringBuilder b = new StringBuilder(); 1227 boolean first = true; 1228 for (String s : list) { 1229 if (first) 1230 first = false; 1231 else 1232 b.append("\r\n" + padLeft("", ' ', indent)); 1233 b.append(s); 1234 } 1235 return b.toString(); 1236 } 1237 1238 1239 public static int countFilesInDirectory(String dirName) { 1240 File dir = new File(dirName); 1241 if (dir.exists() == false) { 1242 return 0; 1243 } 1244 int i = 0; 1245 for (File f : dir.listFiles()) 1246 if (!f.isDirectory()) 1247 i++; 1248 return i; 1249 } 1250 1251 public static String makeId(String name) { 1252 StringBuilder b = new StringBuilder(); 1253 for (char ch : name.toCharArray()) { 1254 if (ch >= 'a' && ch <= 'z') 1255 b.append(ch); 1256 else if (ch >= 'A' && ch <= 'Z') 1257 b.append(ch); 1258 else if (ch >= '0' && ch <= '9') 1259 b.append(ch); 1260 else if (ch == '-' || ch == '.') 1261 b.append(ch); 1262 } 1263 return b.toString(); 1264 } 1265 1266 public interface FileVisitor { 1267 void visitFile(File file) throws FileNotFoundException, IOException; 1268 } 1269 1270 public static void visitFiles(String folder, String extension, FileVisitor visitor) throws FileNotFoundException, IOException { 1271 visitFiles(new File(folder), extension, visitor); 1272 } 1273 1274 public static void visitFiles(File folder, String extension, FileVisitor visitor) throws FileNotFoundException, IOException { 1275 for (File file : folder.listFiles()) { 1276 if (file.isDirectory()) 1277 visitFiles(file, extension, visitor); 1278 else if (extension == null || file.getName().endsWith(extension)) 1279 visitor.visitFile(file); 1280 } 1281 } 1282 1283 public static String extractBaseUrl(String url) { 1284 if (url == null) 1285 return null; 1286 else if (url.contains("/")) 1287 return url.substring(0, url.lastIndexOf("/")); 1288 else 1289 return url; 1290 } 1291 1292 public static String listCanonicalUrls(Set<String> keys) { 1293 return keys.toString(); 1294 } 1295 1296 public static boolean isValidId(String id) { 1297 return id.matches("[A-Za-z0-9\\-\\.]{1,64}"); 1298 } 1299 1300 public static List<String> sorted(Set<String> set) { 1301 List<String> list = new ArrayList<>(); 1302 list.addAll(set); 1303 Collections.sort(list); 1304 return list; 1305 } 1306 1307 public static void analyseStringDiffs(Set<String> source, Set<String> target, Set<String> missed, Set<String> extra) { 1308 for (String s : source) 1309 if (!target.contains(s)) 1310 missed.add(s); 1311 for (String s : target) 1312 if (!source.contains(s)) 1313 extra.add(s); 1314 1315 } 1316 1317 /** 1318 * Only handles simple FHIRPath expressions of the type produced by the validator 1319 * 1320 * @param path 1321 * @return 1322 */ 1323 public static String fhirPathToXPath(String path) { 1324 String[] p = path.split("\\."); 1325 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("."); 1326 int i = 0; 1327 while (i < p.length) { 1328 String s = p[i]; 1329 if (s.contains("[")) { 1330 String si = s.substring(s.indexOf("[") + 1, s.length() - 1); 1331 if (!Utilities.isInteger(si)) 1332 throw new FHIRException("The FHIRPath expression '" + path + "' is not valid"); 1333 s = s.substring(0, s.indexOf("[")) + "[" + Integer.toString(Integer.parseInt(si) + 1) + "]"; 1334 } 1335 if (i < p.length - 1 && p[i + 1].startsWith(".ofType(")) { 1336 i++; 1337 s = s + capitalize(p[i].substring(8, p.length - 1)); 1338 } 1339 b.append(s); 1340 i++; 1341 } 1342 return b.toString(); 1343 } 1344 1345 public static String describeDuration(Duration d) { 1346 if (d.toDays() > 2) { 1347 return String.format("%s days", d.toDays()); 1348 } else if (d.toHours() > 2) { 1349 return String.format("%s hours", d.toHours()); 1350 } else if (d.toMinutes() > 2) { 1351 return String.format("%s mins", d.toMinutes()); 1352 } else { 1353 return String.format("%s ms", d.toMillis()); 1354 } 1355 } 1356 1357 public static boolean startsWithInList(String s, String... list) { 1358 if (s == null) { 1359 return false; 1360 } 1361 for (String l : list) { 1362 if (s.startsWith(l)) { 1363 return true; 1364 } 1365 } 1366 return false; 1367 } 1368 1369 public static boolean startsWithInList(String s, Collection<String> list) { 1370 if (s == null) { 1371 return false; 1372 } 1373 for (String l : list) { 1374 if (s.startsWith(l)) { 1375 return true; 1376 } 1377 } 1378 return false; 1379 } 1380 1381 public static boolean endsWithInList(String s, String... list) { 1382 if (s == null) { 1383 return false; 1384 } 1385 for (String l : list) { 1386 if (s.endsWith(l)) { 1387 return true; 1388 } 1389 } 1390 return false; 1391 } 1392 1393 public static boolean endsWithInList(String s, Collection<String> list) { 1394 if (s == null) { 1395 return false; 1396 } 1397 for (String l : list) { 1398 if (s.endsWith(l)) { 1399 return true; 1400 } 1401 } 1402 return false; 1403 } 1404 1405 public static final int ONE_MB = 1024; 1406 public static final String GB = "Gb"; 1407 public static final String MB = "Mb"; 1408 public static final String KB = "Kb"; 1409 public static final String BT = "b"; 1410 1411 public static String describeSize(int length) { 1412 if (length < 0) throw new IllegalArgumentException("File length of < 0 passed in..."); 1413 1414 if (length > Math.pow(ONE_MB, 3)) { 1415 return length / ((long) Math.pow(ONE_MB, 3)) + GB; 1416 } 1417 if (length > Math.pow(ONE_MB, 2)) { 1418 return length / ((long) Math.pow(ONE_MB, 2)) + MB; 1419 } 1420 if (length > ONE_MB) { 1421 return length / (ONE_MB) + KB; 1422 } 1423 return length + BT; 1424 } 1425 1426 public static String describeSize(long length) { 1427 if (length < 0) throw new IllegalArgumentException("File length of < 0 passed in..."); 1428 1429 if (length > Math.pow(ONE_MB, 3)) { 1430 return length / ((long) Math.pow(ONE_MB, 3)) + GB; 1431 } 1432 if (length > Math.pow(ONE_MB, 2)) { 1433 return length / ((long) Math.pow(ONE_MB, 2)) + MB; 1434 } 1435 if (length > ONE_MB) { 1436 return length / (ONE_MB) + KB; 1437 } 1438 return length + BT; 1439 } 1440 1441 public static List<byte[]> splitBytes(byte[] array, byte[] delimiter) { 1442 List<byte[]> byteArrays = new LinkedList<byte[]>(); 1443 if (delimiter.length == 0) 1444 { 1445 return byteArrays; 1446 } 1447 int begin = 0; 1448 1449 outer: for (int i = 0; i < array.length - delimiter.length + 1; i++) 1450 { 1451 for (int j = 0; j < delimiter.length; j++) 1452 { 1453 if (array[i + j] != delimiter[j]) 1454 { 1455 continue outer; 1456 } 1457 } 1458 1459 // If delimiter is at the beginning then there will not be any data. 1460 if (begin < i) 1461 byteArrays.add(Arrays.copyOfRange(array, begin, i)); 1462 begin = i + delimiter.length; 1463 } 1464 1465 // delimiter at the very end with no data following? 1466 if (begin != array.length) 1467 byteArrays.add(Arrays.copyOfRange(array, begin, array.length)); 1468 1469 return byteArrays; 1470 } 1471 1472 public static void unzip(InputStream zip, String target) throws IOException { 1473 unzip(zip, Path.of(target)); 1474 } 1475 1476 public static void unzip(InputStream zip, Path target) throws IOException { 1477 try (ZipInputStream zis = new ZipInputStream(zip)) { 1478 ZipEntry zipEntry = zis.getNextEntry(); 1479 while (zipEntry != null) { 1480 boolean isDirectory = false; 1481 if (zipEntry.getName().endsWith("/") || zipEntry.getName().endsWith("\\")) { 1482 isDirectory = true; 1483 } 1484 Path newPath = zipSlipProtect(zipEntry, target); 1485 if (isDirectory) { 1486 Files.createDirectories(newPath); 1487 } else { 1488 if (newPath.getParent() != null) { 1489 if (Files.notExists(newPath.getParent())) { 1490 Files.createDirectories(newPath.getParent()); 1491 } 1492 } 1493 Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING); 1494 } 1495 zipEntry = zis.getNextEntry(); 1496 } 1497 zis.closeEntry(); 1498 } 1499 } 1500 1501 public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) 1502 throws IOException { 1503 1504 // test zip slip vulnerability 1505 // Path targetDirResolved = targetDir.resolve("../../" + zipEntry.getName()); 1506 1507 Path targetDirResolved = targetDir.resolve(zipEntry.getName()); 1508 1509 // make sure normalized file still has targetDir as its prefix 1510 // else throws exception 1511 Path normalizePath = targetDirResolved.normalize(); 1512 if (!normalizePath.startsWith(targetDir)) { 1513 throw new IOException("Bad zip entry: " + zipEntry.getName()); 1514 } 1515 1516 return normalizePath; 1517 } 1518 1519 final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47}; 1520 1521 static { 1522 Arrays.sort(illegalChars); 1523 } 1524 1525 public static String cleanFileName(String badFileName) { 1526 StringBuilder cleanName = new StringBuilder(); 1527 int len = badFileName.codePointCount(0, badFileName.length()); 1528 for (int i=0; i<len; i++) { 1529 int c = badFileName.codePointAt(i); 1530 if (Arrays.binarySearch(illegalChars, c) < 0) { 1531 cleanName.appendCodePoint(c); 1532 } 1533 } 1534 return cleanName.toString(); 1535 } 1536 1537 public static boolean isValidUUID(String uuid) { 1538 return uuid.matches(UUID_REGEX); 1539 } 1540 1541 public static boolean isValidOID(String oid) { 1542 return oid.matches(OID_REGEX); 1543 } 1544 1545 public static int findinList(String[] list, String val) { 1546 for (int i = 0; i < list.length; i++) { 1547 if (val.equals(list[i])) { 1548 return i; 1549 } 1550 } 1551 return -1; 1552 } 1553 1554 public static String toString(String[] expected) { 1555 return "['"+String.join("' | '", expected)+"']"; 1556 } 1557 1558 public static String lowBoundaryForDecimal(String value, int precision) { 1559 if (Utilities.noString(value)) { 1560 throw new FHIRException("Unable to calculate lowBoundary for a null decimal string"); 1561 } 1562 String e = value.contains("e") ? value.substring(value.indexOf("e")+1) : null; 1563 if (value.contains("e")) { 1564 value = value.substring(0, value.indexOf("e")); 1565 } 1566 if (isZero(value)) { 1567 return applyPrecision("-0.5000000000000000000000000", precision); 1568 } else if (value.startsWith("-")) { 1569 return "-"+highBoundaryForDecimal(value.substring(1), precision)+(e == null ? "" : e); 1570 } else { 1571 if (value.contains(".")) { 1572 return applyPrecision(minusOne(value)+"50000000000000000000000000000", precision)+(e == null ? "" : e); 1573 } else { 1574 return applyPrecision(minusOne(value)+".50000000000000000000000000000", precision)+(e == null ? "" : e); 1575 } 1576 } 1577 } 1578 1579 private static String applyPrecision(String v, int p) { 1580 int d = p - getDecimalPrecision(v); 1581 if (d == 0) { 1582 return v; 1583 } else if (d > 0) { 1584 return v + padLeft("", '0', d); 1585 } else { 1586 if (v.charAt(v.length()+d) >= '6') { 1587 return v.substring(0, v.length()+d-1)+((char) (v.charAt(v.length()+d)+1)); 1588 } else { 1589 return v.substring(0, v.length()+d); 1590 } 1591 } 1592 } 1593 1594 private static String minusOne(String value) { 1595 StringBuffer s = new StringBuffer(value); 1596 for (int i = s.length()-1; i >= 0; i--) { 1597 if (s.charAt(i) == '0') { 1598 s.setCharAt(i, '9'); 1599 } else if (s.charAt(i) != '.') { 1600 s.setCharAt(i, (char) (s.charAt(i)-1)); 1601 break; 1602 } 1603 } 1604 return s.toString(); 1605 } 1606 1607 public static String lowBoundaryForDate(String value, int precision) { 1608 String[] res = splitTimezone(value); 1609 StringBuilder b = new StringBuilder(res[0]); 1610 if (b.length() == 4) { 1611 b.append("-01"); 1612 } 1613 if (b.length() == 7) { 1614 b.append("-01"); 1615 } 1616 if (b.length() == 10) { 1617 b.append("T00:00"); 1618 } 1619 if (b.length() == 16) { 1620 b.append(":00"); 1621 } 1622 if (b.length() == 19) { 1623 b.append(".000"); 1624 } 1625 return applyDatePrecision(b.toString(), precision)+res[1]; 1626 } 1627 1628 public static String lowBoundaryForTime(String value, int precision) { 1629 String[] res = splitTimezone(value); 1630 StringBuilder b = new StringBuilder(res[0]); 1631 if (b.length() == 2) { 1632 b.append(":00"); 1633 } 1634 if (b.length() == 5) { 1635 b.append(":00"); 1636 } 1637 if (b.length() == 8) { 1638 b.append(".000"); 1639 } 1640 return applyTimePrecision(b.toString(), precision)+res[1]; 1641 } 1642 1643 public static String highBoundaryForTime(String value, int precision) { 1644 String[] res = splitTimezone(value); 1645 StringBuilder b = new StringBuilder(res[0]); 1646 if (b.length() == 2) { 1647 b.append(":59"); 1648 } 1649 if (b.length() == 5) { 1650 b.append(":59"); 1651 } 1652 if (b.length() == 8) { 1653 b.append(".999"); 1654 } 1655 return applyTimePrecision(b.toString(), precision)+res[1]; 1656 } 1657 1658 1659 private static Object applyDatePrecision(String v, int precision) { 1660 switch (precision) { 1661 case 4: return v.substring(0, 4); 1662 case 6: return v.substring(0, 7); 1663 case 8: return v.substring(0, 10); 1664 case 14: return v.substring(0, 17); 1665 case 17: return v; 1666 } 1667 throw new FHIRException("Unsupported Date precision for boundary operation: "+precision); 1668 } 1669 1670 private static Object applyTimePrecision(String v, int precision) { 1671 switch (precision) { 1672 case 2: return v.substring(0, 3); 1673 case 4: return v.substring(0, 6); 1674 case 6: return v.substring(0, 9); 1675 case 9: return v; 1676 } 1677 throw new FHIRException("Unsupported Time precision for boundary operation: "+precision); 1678 } 1679 1680 public static String highBoundaryForDecimal(String value, int precision) { 1681 if (Utilities.noString(value)) { 1682 throw new FHIRException("Unable to calculate highBoundary for a null decimal string"); 1683 } 1684 String e = value.contains("e") ? value.substring(value.indexOf("e")+1) : null; 1685 if (value.contains("e")) { 1686 value = value.substring(0, value.indexOf("e")); 1687 } 1688 if (isZero(value)) { 1689 return applyPrecision("0.50000000000000000000000000000", precision); 1690 } else if (value.startsWith("-")) { 1691 return "-"+lowBoundaryForDecimal(value.substring(1), precision)+(e == null ? "" : e); 1692 } else { 1693 if (value.contains(".")) { 1694 return applyPrecision(value+"50000000000000000000000000000", precision)+(e == null ? "" : e); 1695 } else { 1696 return applyPrecision(value+".50000000000000000000000000000", precision)+(e == null ? "" : e); 1697 } 1698 } 1699 } 1700 1701 private static boolean isZero(String value) { 1702 return value.replace(".", "").replace("-", "").replace("0", "").length() == 0; 1703 } 1704 1705 public static String highBoundaryForDate(String value, int precision) { 1706 String[] res = splitTimezone(value); 1707 StringBuilder b = new StringBuilder(res[0]); 1708 if (b.length() == 4) { 1709 b.append("-12"); 1710 } 1711 if (b.length() == 7) { 1712 b.append("-"+dayCount(Integer.parseInt(b.substring(0,4)), Integer.parseInt(b.substring(5,7)))); 1713 } 1714 if (b.length() == 10) { 1715 b.append("T23:59"); 1716 } 1717 if (b.length() == 16) { 1718 b.append(":59"); 1719 } 1720 if (b.length() == 19) { 1721 b.append(".999"); 1722 } 1723 return applyDatePrecision(b.toString(), precision)+res[1]; 1724 } 1725 1726 private static String dayCount(int y, int m) { 1727 switch (m) { 1728 case 1: return "31"; 1729 case 2: return ((y % 4 == 0) && (y % 400 == 0 || !(y % 100 == 0))) ? "29" : "28"; 1730 case 3: return "31"; 1731 case 4: return "30"; 1732 case 5: return "31"; 1733 case 6: return "30"; 1734 case 7: return "31"; 1735 case 8: return "31"; 1736 case 9: return "30"; 1737 case 10: return "31"; 1738 case 11: return "30"; 1739 case 12: return "31"; 1740 default: return "30"; // make the compiler happy 1741 } 1742 } 1743 1744 public static Integer getDecimalPrecision(String value) { 1745 if (value.contains("e")) { 1746 value = value.substring(0, value.indexOf("e")); 1747 } 1748 if (value.contains(".")) { 1749 return value.split("\\.")[1].length(); 1750 } else { 1751 return 0; 1752 } 1753 } 1754 1755 1756 private static String[] splitTimezone(String value) { 1757 String[] res = new String[2]; 1758 1759 if (value.contains("+")) { 1760 res[0] = value.substring(0, value.indexOf("+")); 1761 res[1] = value.substring(value.indexOf("+")); 1762 } else if (value.contains("-") && value.contains("T") && value.lastIndexOf("-") > value.indexOf("T")) { 1763 res[0] = value.substring(0, value.lastIndexOf("-")); 1764 res[1] = value.substring(value.lastIndexOf("-")); 1765 } else if (value.contains("Z")) { 1766 res[0] = value.substring(0, value.indexOf("Z")); 1767 res[1] = value.substring(value.indexOf("Z")); 1768 } else { 1769 res[0] = value; 1770 res[1] = ""; 1771 } 1772 return res; 1773 } 1774 1775 public static Integer getDatePrecision(String value) { 1776 return splitTimezone(value)[0].replace("-", "").replace("T", "").replace(":", "").replace(".", "").length(); 1777 } 1778 1779 public static Integer getTimePrecision(String value) { 1780 return splitTimezone(value)[0].replace("T", "").replace(":", "").replace(".", "").length(); 1781 } 1782 1783 public static String padInt(int i, int len) { 1784 return Utilities.padLeft(Integer.toString(i), ' ', len); 1785 } 1786 1787 public static String padInt(long i, int len) { 1788 return Utilities.padLeft(Long.toString(i), ' ', len); 1789 } 1790 1791 public static Object makeSingleLine(String text) { 1792 text = text.replace("\r", " "); 1793 text = text.replace("\n", " "); 1794 while (text.contains(" ")) { 1795 text = text.replace(" ", " "); 1796 } 1797 return text; 1798 } 1799 1800 public static int parseInt(String value, int def) { 1801 if (isInteger(value)) { 1802 return Integer.parseInt(value); 1803 } else { 1804 return def; 1805 } 1806 } 1807 1808 /** 1809 * Appends a text from a derived element to its base element. 1810 * 1811 * @param baseText The text set in the base element, or {@code null}. 1812 * @param derivedText The text set in the derived element, starting with "...". 1813 * @return The resulting text. 1814 */ 1815 public static String appendDerivedTextToBase(@Nullable final String baseText, 1816 final String derivedText) { 1817 if (baseText == null) { 1818 return derivedText.substring(3); 1819 } 1820 return baseText + "\r\n" + derivedText.substring(3); 1821 } 1822 1823 public static void deleteEmptyFolders(File df) { 1824 for (File f : df.listFiles()) { 1825 if (f.isDirectory()) { 1826 deleteEmptyFolders(f); 1827 } 1828 } 1829 boolean empty = true; 1830 for (File f : df.listFiles()) { 1831 empty = false; 1832 break; 1833 } 1834 if (empty) { 1835 df.delete(); 1836 } 1837 } 1838 1839 public static String getRelativePath(String root, String path) { 1840 String res = path.substring(root.length()); 1841 if (res.startsWith(File.separator)) { 1842 res = res.substring(1); 1843 } 1844 return res; 1845 } 1846 1847 public static List<String> listAllFiles(String path, List<String> ignoreList) { 1848 List<String> res = new ArrayList<>(); 1849 addAllFiles(res, path, new File(path), ignoreList); 1850 return res; 1851 } 1852 1853 private static void addAllFiles(List<String> res, String root, File dir, List<String> ignoreList) { 1854 for (File f : dir.listFiles()) { 1855 if (ignoreList == null || !ignoreList.contains(f.getAbsolutePath())) { 1856 if (f.isDirectory()) { 1857 addAllFiles(res, root, f, ignoreList); 1858 } else { 1859 res.add(getRelativePath(root, f.getAbsolutePath())); 1860 } 1861 } 1862 } 1863 1864 } 1865}