1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.BufferedReader; 25 import java.io.File; 26 import java.io.FileNotFoundException; 27 import java.io.FileWriter; 28 import java.io.IOException; 29 import java.io.InputStreamReader; 30 import java.io.PrintWriter; 31 import java.io.StringWriter; 32 import java.net.URI; 33 import java.nio.charset.Charset; 34 import java.nio.file.Files; 35 import java.nio.file.Path; 36 import java.nio.file.Paths; 37 import java.nio.file.StandardOpenOption; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collection; 41 import java.util.Collections; 42 import java.util.EnumSet; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.Set; 46 import java.util.regex.Matcher; 47 import java.util.regex.Pattern; 48 49 import javax.tools.JavaCompiler; 50 import javax.tools.JavaFileObject; 51 import javax.tools.SimpleJavaFileObject; 52 import javax.tools.ToolProvider; 53 54 import com.sun.source.util.JavacTask; 55 import com.sun.tools.javac.api.JavacTaskImpl; 56 57 import sun.tools.jar.Main; 58 59 import static java.nio.file.StandardCopyOption.*; 60 61 /** 62 * Toolbox for jtreg tests. 63 */ 64 65 public class ToolBox { 66 67 public static final String lineSeparator = System.getProperty("line.separator"); 68 public static final String jdkUnderTest = System.getProperty("test.jdk"); 69 public static final Path javaBinary = Paths.get(jdkUnderTest, "bin", "java"); 70 public static final Path javacBinary = Paths.get(jdkUnderTest, "bin", "javac"); 71 72 public static final List<String> testToolVMOpts; 73 public static final List<String> testVMOpts; 74 75 private static final Charset defaultCharset = Charset.defaultCharset(); 76 77 static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); 78 79 static { 80 String sysProp = System.getProperty("test.tool.vm.opts"); 81 if (sysProp != null && sysProp.length() > 0) { 82 testToolVMOpts = Arrays.asList(sysProp.split("\\s+")); 83 } else { 84 testToolVMOpts = Collections.<String>emptyList(); 85 } 86 87 sysProp = System.getProperty("test.vm.opts"); 88 if (sysProp != null && sysProp.length() > 0) { 89 testVMOpts = Arrays.asList(sysProp.split("\\s+")); 90 } else { 91 testVMOpts = Collections.<String>emptyList(); 92 } 93 } 94 95 /** 96 * The expected result of command-like method execution. 97 */ 98 public enum Expect {SUCCESS, FAIL} 99 100 enum AcceptedParams { 101 EXPECT, 102 SOURCES, 103 OPTIONS, 104 STD_OUTPUT, 105 ERR_OUTPUT, 106 EXTRA_ENV, 107 } 108 109 enum OutputKind {STD, ERR} 110 111 /** 112 * Helper class to abstract the processing of command's output. 113 */ 114 static abstract class WriterHelper { 115 OutputKind kind; pipeOutput(ProcessBuilder pb)116 public abstract void pipeOutput(ProcessBuilder pb); readFromStream(Process p)117 public abstract void readFromStream(Process p) throws IOException; addAll(Collection<? extends String> c)118 public abstract void addAll(Collection<? extends String> c) throws IOException; 119 } 120 121 /** 122 * Helper class for redirecting command's output to a file. 123 */ 124 static class FileWriterHelper extends WriterHelper { 125 File file; 126 FileWriterHelper(File file, OutputKind kind)127 FileWriterHelper(File file, OutputKind kind) { 128 this.file = file; 129 this.kind = kind; 130 } 131 132 @Override pipeOutput(ProcessBuilder pb)133 public void pipeOutput(ProcessBuilder pb) { 134 if (file != null) { 135 switch (kind) { 136 case STD: 137 pb.redirectInput(file); 138 break; 139 case ERR: 140 pb.redirectError(file); 141 break; 142 } 143 } 144 } 145 146 @Override readFromStream(Process p)147 public void readFromStream(Process p) throws IOException {} 148 149 @Override addAll(Collection<? extends String> c)150 public void addAll(Collection<? extends String> c) throws IOException { 151 if (file.exists()) 152 Files.write(file.toPath(), c, defaultCharset, 153 StandardOpenOption.WRITE, StandardOpenOption.APPEND); 154 else 155 Files.write(file.toPath(), c, defaultCharset); 156 } 157 } 158 159 /** 160 * Helper class for redirecting command's output to a String list. 161 */ 162 static class ListWriterHelper extends WriterHelper { 163 List<String> list; 164 ListWriterHelper(List<String> list, OutputKind kind)165 public ListWriterHelper(List<String> list, OutputKind kind) { 166 this.kind = kind; 167 this.list = list; 168 } 169 170 @Override pipeOutput(ProcessBuilder pb)171 public void pipeOutput(ProcessBuilder pb) {} 172 173 @Override readFromStream(Process p)174 public void readFromStream(Process p) throws IOException { 175 BufferedReader br = null; 176 switch (kind) { 177 case STD: 178 br = new BufferedReader(new InputStreamReader(p.getInputStream())); 179 break; 180 case ERR: 181 br = new BufferedReader(new InputStreamReader(p.getErrorStream())); 182 break; 183 } 184 String line; 185 while ((line = br.readLine()) != null) { 186 list.add(line); 187 } 188 } 189 addAll(Collection<? extends String> c)190 public void addAll(Collection<? extends String> c) { 191 list.addAll(c); 192 } 193 } 194 195 /** 196 * Simple factory class for creating a WriterHelper instance. 197 */ 198 static class WriterHelperFactory { make(File file, OutputKind kind)199 static WriterHelper make(File file, OutputKind kind) { 200 return new FileWriterHelper(file, kind); 201 } 202 make(List<String> list, OutputKind kind)203 static WriterHelper make(List<String> list, OutputKind kind) { 204 return new ListWriterHelper(list, kind); 205 } 206 } 207 208 /** 209 * A generic class for holding command's arguments. 210 */ 211 public static abstract class GenericArgs <T extends GenericArgs> { 212 protected static List<Set<AcceptedParams>> minAcceptedParams; 213 214 protected Set<AcceptedParams> currentParams = 215 EnumSet.<AcceptedParams>noneOf(AcceptedParams.class); 216 217 protected Expect whatToExpect; 218 protected WriterHelper stdOutput; 219 protected WriterHelper errOutput; 220 protected List<String> args = new ArrayList<>(); 221 protected String[] argsArr; 222 GenericArgs()223 protected GenericArgs() { 224 set(Expect.SUCCESS); 225 } 226 set(Expect whatToExpt)227 public T set(Expect whatToExpt) { 228 currentParams.add(AcceptedParams.EXPECT); 229 this.whatToExpect = whatToExpt; 230 return (T)this; 231 } 232 setStdOutput(List<String> stdOutput)233 public T setStdOutput(List<String> stdOutput) { 234 currentParams.add(AcceptedParams.STD_OUTPUT); 235 this.stdOutput = WriterHelperFactory.make(stdOutput, OutputKind.STD); 236 return (T)this; 237 } 238 setStdOutput(File output)239 public T setStdOutput(File output) { 240 currentParams.add(AcceptedParams.STD_OUTPUT); 241 this.stdOutput = WriterHelperFactory.make(output, OutputKind.STD); 242 return (T)this; 243 } 244 setErrOutput(List<String> errOutput)245 public T setErrOutput(List<String> errOutput) { 246 currentParams.add(AcceptedParams.ERR_OUTPUT); 247 this.errOutput = WriterHelperFactory.make(errOutput, OutputKind.ERR); 248 return (T)this; 249 } 250 setErrOutput(File errOutput)251 public T setErrOutput(File errOutput) { 252 currentParams.add(AcceptedParams.ERR_OUTPUT); 253 this.errOutput = WriterHelperFactory.make(errOutput, OutputKind.ERR); 254 return (T)this; 255 } 256 setAllArgs(String... args)257 public T setAllArgs(String... args) { 258 currentParams.add(AcceptedParams.OPTIONS); 259 this.argsArr = args; 260 return (T) this; 261 } 262 263 appendArgs(String... args)264 public T appendArgs(String... args) { 265 appendArgs(Arrays.asList(args)); 266 return (T)this; 267 } 268 appendArgs(Path... args)269 public T appendArgs(Path... args) { 270 if (args != null) { 271 List<String> list = new ArrayList<>(); 272 for (int i = 0; i < args.length; i++) { 273 if (args[i] != null) { 274 list.add(args[i].toString()); 275 } 276 } 277 appendArgs(list); 278 } 279 return (T)this; 280 } 281 appendArgs(List<String> args)282 public T appendArgs(List<String> args) { 283 if (args != null && args.size() > 0) { 284 currentParams.add(AcceptedParams.OPTIONS); 285 for (int i = 0; i < args.size(); i++) { 286 if (args.get(i) != null) { 287 this.args.add(args.get(i)); 288 } 289 } 290 } 291 return (T)this; 292 } 293 setOptions(List<String> options)294 public T setOptions(List<String> options) { 295 currentParams.add(AcceptedParams.OPTIONS); 296 this.args = options; 297 return (T)this; 298 } 299 setOptions(String... options)300 public T setOptions(String... options) { 301 currentParams.add(AcceptedParams.OPTIONS); 302 this.args = Arrays.asList(options); 303 return (T)this; 304 } 305 hasMinParams()306 public boolean hasMinParams() { 307 for (Set<AcceptedParams> minSet : minAcceptedParams) { 308 if (currentParams.containsAll(minSet)) { 309 return true; 310 } 311 } 312 return false; 313 } 314 } 315 316 /** 317 * A more specific class for holding javac-like command's arguments. 318 */ 319 public static class JavaToolArgs extends GenericArgs<JavaToolArgs> { 320 321 static { 322 minAcceptedParams = new ArrayList<>(); of(AcceptedParams.EXPECT, AcceptedParams.OPTIONS)323 minAcceptedParams.add(EnumSet.<AcceptedParams>of( 324 AcceptedParams.EXPECT, AcceptedParams.OPTIONS)); of(AcceptedParams.EXPECT, AcceptedParams.SOURCES)325 minAcceptedParams.add(EnumSet.<AcceptedParams>of( 326 AcceptedParams.EXPECT, AcceptedParams.SOURCES)); 327 } 328 329 protected List<? extends JavaFileObject> sources; 330 JavaToolArgs()331 public JavaToolArgs() { 332 super(); 333 } 334 JavaToolArgs(Expect whatToExpt)335 public JavaToolArgs(Expect whatToExpt) { 336 super.set(whatToExpt); 337 } 338 setSources(List<? extends JavaFileObject> sources)339 public JavaToolArgs setSources(List<? extends JavaFileObject> sources) { 340 currentParams.add(AcceptedParams.SOURCES); 341 this.sources = sources; 342 return this; 343 } 344 setSources(JavaSource... sources)345 public JavaToolArgs setSources(JavaSource... sources) { 346 return setSources(Arrays.asList(sources)); 347 } 348 setSources(String... sources)349 public JavaToolArgs setSources(String... sources) { 350 List<JavaSource> javaSrcs = new ArrayList<>(); 351 for (String source : sources) { 352 javaSrcs.add(new JavaSource(source)); 353 } 354 return setSources(javaSrcs); 355 } 356 } 357 358 /** 359 * A more specific class for holding any command's arguments. 360 */ 361 public static class AnyToolArgs extends GenericArgs<AnyToolArgs> { 362 363 static { 364 minAcceptedParams = new ArrayList<>(); of(AcceptedParams.EXPECT, AcceptedParams.OPTIONS)365 minAcceptedParams.add(EnumSet.<AcceptedParams>of( 366 AcceptedParams.EXPECT, AcceptedParams.OPTIONS)); 367 } 368 369 Map<String, String> extraEnv; 370 AnyToolArgs()371 public AnyToolArgs() { 372 super(); 373 } 374 AnyToolArgs(Expect whatToExpt)375 public AnyToolArgs(Expect whatToExpt) { 376 set(whatToExpt); 377 } 378 set(Map<String, String> extraEnv)379 public AnyToolArgs set(Map<String, String> extraEnv) { 380 currentParams.add(AcceptedParams.EXTRA_ENV); 381 this.extraEnv = extraEnv; 382 return this; 383 } 384 } 385 386 /** 387 * Custom exception for bad command execution. 388 */ 389 public static class CommandExecutionException extends Exception { CommandExecutionException(List<String> command, Expect whatToExpt)390 CommandExecutionException(List<String> command, Expect whatToExpt) { 391 super(createMessage(command, whatToExpt)); 392 } 393 CommandExecutionException(Expect whatToExpt, String... command)394 CommandExecutionException(Expect whatToExpt, String... command) { 395 this(Arrays.asList(command), whatToExpt); 396 } 397 createMessage(List<String> command, Expect whatToExpt)398 private static String createMessage(List<String> command, Expect whatToExpt) { 399 StringBuilder sb = new StringBuilder().append("Command : "); 400 sb.append(command.toString()).append(lineSeparator); 401 switch (whatToExpt) { 402 case SUCCESS: 403 sb.append(" has unexpectedly failed"); 404 break; 405 case FAIL: 406 sb.append(" has been unexpectedly successful"); 407 break; 408 } 409 return sb.toString(); 410 } 411 } 412 413 /** 414 * Custom exception for not equal resources. 415 */ 416 public static class ResourcesNotEqualException extends Exception { ResourcesNotEqualException(List<String> res1, List<String> res2)417 public ResourcesNotEqualException(List<String> res1, List<String> res2) { 418 super(createMessage(res1, res2)); 419 } 420 ResourcesNotEqualException(String line1, String line2)421 public ResourcesNotEqualException(String line1, String line2) { 422 super(createMessage(line1, line2)); 423 } 424 ResourcesNotEqualException(Path path1, Path path2)425 public ResourcesNotEqualException(Path path1, Path path2) { 426 super(createMessage(path1, path2)); 427 } 428 createMessage(Path path1, Path path2)429 private static String createMessage(Path path1, Path path2) { 430 return new StringBuilder() 431 .append("The resources provided for comparison in paths \n") 432 .append(path1.toString()).append(" and \n") 433 .append(path2.toString()).append("are different").toString(); 434 } 435 createMessage(String line1, String line2)436 private static String createMessage(String line1, String line2) { 437 return new StringBuilder() 438 .append("The resources provided for comparison are different at lines: \n") 439 .append(line1).append(" and \n") 440 .append(line2).toString(); 441 } 442 createMessage(List<String> res1, List<String> res2)443 private static String createMessage(List<String> res1, List<String> res2) { 444 return new StringBuilder() 445 .append("The resources provided for comparison are different: \n") 446 .append("Resource 1 is: ").append(res1).append("\n and \n") 447 .append("Resource 2 is: ").append(res2).append("\n").toString(); 448 } 449 } 450 451 /** 452 * A javac compiler caller method. 453 */ javac(JavaToolArgs params)454 public static int javac(JavaToolArgs params) 455 throws CommandExecutionException, IOException { 456 if (params.hasMinParams()) { 457 if (params.argsArr != null) { 458 return genericJavaCMD(JavaCMD.JAVAC, params); 459 } else { 460 return genericJavaCMD(JavaCMD.JAVAC_API, params); 461 } 462 } 463 throw new AssertionError("javac command has been invoked with less parameters than needed"); 464 } 465 466 /** 467 * A javap calling method. 468 */ javap(JavaToolArgs params)469 public static String javap(JavaToolArgs params) 470 throws CommandExecutionException, IOException { 471 if (params.hasMinParams()) { 472 List<String> list = new ArrayList<>(); 473 params.setErrOutput(list); 474 genericJavaCMD(JavaCMD.JAVAP, params); 475 return listToString(list); 476 } 477 throw new AssertionError("javap command has been invoked with less parameters than needed"); 478 } 479 480 /** 481 * A javah calling method. 482 */ javah(JavaToolArgs params)483 public static int javah(JavaToolArgs params) 484 throws CommandExecutionException, IOException { 485 if (params.hasMinParams()) { 486 return genericJavaCMD(JavaCMD.JAVAH, params); 487 } 488 throw new AssertionError("javah command has been invoked with less parameters than needed"); 489 } 490 491 /** 492 * A enum class for langtools commands. 493 */ 494 enum JavaCMD { 495 JAVAC { 496 @Override run(JavaToolArgs params, PrintWriter pw)497 int run(JavaToolArgs params, PrintWriter pw) { 498 return com.sun.tools.javac.Main.compile(params.argsArr, pw); 499 } 500 }, 501 JAVAC_API { 502 @Override run(JavaToolArgs params, PrintWriter pw)503 int run(JavaToolArgs params, PrintWriter pw) { 504 JavacTask ct = (JavacTask)comp.getTask(pw, null, null, 505 params.args, null, params.sources); 506 return ((JavacTaskImpl)ct).doCall().exitCode; 507 } 508 509 @Override getName()510 String getName() { 511 return "javac"; 512 } 513 514 @Override getExceptionMsgContent(JavaToolArgs params)515 List<String> getExceptionMsgContent(JavaToolArgs params) { 516 List<String> result = super.getExceptionMsgContent(params); 517 for (JavaFileObject source : params.sources) { 518 if (source instanceof JavaSource) { 519 result.add(((JavaSource)source).name); 520 } 521 } 522 return result; 523 } 524 }, 525 JAVAH { 526 @Override run(JavaToolArgs params, PrintWriter pw)527 int run(JavaToolArgs params, PrintWriter pw) { 528 return com.sun.tools.javah.Main.run(params.argsArr, pw); 529 } 530 }, 531 JAVAP { 532 @Override run(JavaToolArgs params, PrintWriter pw)533 int run(JavaToolArgs params, PrintWriter pw) { 534 return com.sun.tools.javap.Main.run(params.argsArr, pw); 535 } 536 }; 537 run(JavaToolArgs params, PrintWriter pw)538 abstract int run(JavaToolArgs params, PrintWriter pw); 539 getName()540 String getName() { 541 return this.name().toLowerCase(); 542 } 543 getExceptionMsgContent(JavaToolArgs params)544 List<String> getExceptionMsgContent(JavaToolArgs params) { 545 List<String> result = new ArrayList<>(); 546 result.add(getName()); 547 result.addAll(params.argsArr != null ? 548 Arrays.asList(params.argsArr) : 549 params.args); 550 return result; 551 } 552 } 553 554 /** 555 * A helper method for executing langtools commands. 556 */ genericJavaCMD( JavaCMD cmd, JavaToolArgs params)557 private static int genericJavaCMD( 558 JavaCMD cmd, 559 JavaToolArgs params) 560 throws CommandExecutionException, IOException { 561 int rc = 0; 562 StringWriter sw = null; 563 try (PrintWriter pw = (params.errOutput == null) ? 564 null : new PrintWriter(sw = new StringWriter())) { 565 rc = cmd.run(params, pw); 566 } 567 String out = (sw == null) ? null : sw.toString(); 568 569 if (params.errOutput != null && (out != null) && !out.isEmpty()) { 570 params.errOutput.addAll(splitLines(out, lineSeparator)); 571 } 572 573 if ( (rc == 0 && params.whatToExpect == Expect.SUCCESS) || 574 (rc != 0 && params.whatToExpect == Expect.FAIL) ) { 575 return rc; 576 } 577 578 throw new CommandExecutionException(cmd.getExceptionMsgContent(params), 579 params.whatToExpect); 580 } 581 582 /** 583 * A jar calling method. 584 */ jar(String... params)585 public static boolean jar(String... params) throws CommandExecutionException { 586 Main jarGenerator = new Main(System.out, System.err, "jar"); 587 boolean result = jarGenerator.run(params); 588 if (!result) { 589 List<String> command = new ArrayList<>(); 590 command.add("jar"); 591 command.addAll(Arrays.asList(params)); 592 throw new CommandExecutionException(command, Expect.SUCCESS); 593 } 594 return result; 595 } 596 597 /** 598 * A general command calling method. 599 */ executeCommand(AnyToolArgs params)600 public static int executeCommand(AnyToolArgs params) 601 throws CommandExecutionException, IOException, InterruptedException { 602 if (params.hasMinParams()) { 603 List<String> cmd = (params.args != null) ? 604 params.args : 605 Arrays.asList(params.argsArr); 606 return executeCommand(cmd, params.extraEnv, params.stdOutput, 607 params.errOutput, params.whatToExpect); 608 } 609 throw new AssertionError("command has been invoked with less parameters than needed"); 610 } 611 612 /** 613 * A helper method for calling a general command. 614 */ executeCommand( List<String> command, Map<String, String> extraEnv, WriterHelper stdOutput, WriterHelper errOutput, Expect whatToExpt)615 private static int executeCommand( 616 List<String> command, 617 Map<String, String> extraEnv, 618 WriterHelper stdOutput, 619 WriterHelper errOutput, 620 Expect whatToExpt) 621 throws IOException, InterruptedException, CommandExecutionException { 622 ProcessBuilder pb = new ProcessBuilder(command); 623 624 if (stdOutput != null) stdOutput.pipeOutput(pb); 625 if (errOutput != null) errOutput.pipeOutput(pb); 626 627 if (extraEnv != null) { 628 pb.environment().putAll(extraEnv); 629 } 630 631 Process p = pb.start(); 632 633 if (stdOutput != null) stdOutput.readFromStream(p); 634 if (errOutput != null) errOutput.readFromStream(p); 635 636 int result = p.waitFor(); 637 if ( (result == 0 && whatToExpt == Expect.SUCCESS) || 638 (result != 0 && whatToExpt == Expect.FAIL) ) { 639 return result; 640 } 641 642 throw new CommandExecutionException(command, whatToExpt); 643 } 644 645 /** 646 * This set of methods can be used instead of diff when the only needed 647 * result is the equality or inequality of the two given resources. 648 * 649 * A resource can be a file or a String list. 650 */ compareLines(Path aPath, Path otherPath, String encoding)651 public static void compareLines(Path aPath, Path otherPath, String encoding) 652 throws FileNotFoundException, IOException, ResourcesNotEqualException { 653 compareLines(aPath, otherPath, encoding, false); 654 } 655 compareLines( Path aPath, Path otherPath, String encoding, boolean trim)656 public static void compareLines( 657 Path aPath, Path otherPath, String encoding, boolean trim) 658 throws FileNotFoundException, IOException, ResourcesNotEqualException { 659 Charset charset = encoding != null ? 660 Charset.forName(encoding) : 661 defaultCharset; 662 List<String> list1 = Files.readAllLines(aPath, charset); 663 List<String> list2 = Files.readAllLines(otherPath, charset); 664 compareLines(list1, list2, trim); 665 } 666 compareLines(Path path, List<String> strings, String encoding)667 public static void compareLines(Path path, List<String> strings, String encoding) 668 throws FileNotFoundException, IOException, ResourcesNotEqualException { 669 compareLines(path, strings, encoding, false); 670 } 671 compareLines(Path path, List<String> strings, String encoding, boolean trim)672 public static void compareLines(Path path, List<String> strings, 673 String encoding, boolean trim) 674 throws FileNotFoundException, IOException, ResourcesNotEqualException { 675 Charset charset = encoding != null ? 676 Charset.forName(encoding) : 677 defaultCharset; 678 List<String> list = Files.readAllLines(path, charset); 679 compareLines(list, strings, trim); 680 } 681 compareLines(List<String> list1, List<String> list2)682 public static void compareLines(List<String> list1, List<String> list2) 683 throws ResourcesNotEqualException { 684 compareLines(list1, list2, false); 685 } 686 compareLines(List<String> list1, List<String> list2, boolean trim)687 public static void compareLines(List<String> list1, 688 List<String> list2, boolean trim) throws ResourcesNotEqualException { 689 if ((list1 == list2) || (list1 == null && list2 == null)) return; 690 if (list1.size() != list2.size()) 691 throw new ResourcesNotEqualException(list1, list2); 692 int i = 0; 693 int j = 0; 694 while (i < list1.size() && 695 j < list2.size() && 696 equals(list1.get(i), list2.get(j), trim)) { 697 i++; j++; 698 } 699 if (!(i == list1.size() && j == list2.size())) 700 throw new ResourcesNotEqualException(list1, list2); 701 } 702 equals(String s1, String s2, boolean trim)703 private static boolean equals(String s1, String s2, boolean trim) { 704 return (trim ? s1.trim().equals(s2.trim()) : s1.equals(s2)); 705 } 706 707 /** 708 * A set of simple grep-like methods, looks for regExpr in text. 709 * The content of text is split using the new line character as a pattern 710 * and later the regExpr is seek in every split line. If a match is found, 711 * the whole line is added to the result. 712 */ grep(String regExpr, String text, String sep)713 public static List<String> grep(String regExpr, String text, String sep) { 714 return grep(regExpr, splitLines(text, sep)); 715 } 716 grep(String regExpr, List<String> text)717 public static List<String> grep(String regExpr, List<String> text) { 718 List<String> result = new ArrayList<>(); 719 Pattern pattern = Pattern.compile(regExpr); 720 for (String s : text) { 721 if (pattern.matcher(s).find()) { 722 result.add(s); 723 } 724 } 725 return result; 726 } 727 grep(String regExpr, File f)728 public static List<String> grep(String regExpr, File f) 729 throws IOException { 730 List<String> lines = Files.readAllLines(f.toPath(), defaultCharset); 731 return grep(regExpr, lines); 732 } 733 734 /** 735 * A touch-like method. 736 */ touch(String fileName)737 public static boolean touch(String fileName) { 738 File file = new File(fileName); 739 return touch(file); 740 } 741 touch(File file)742 public static boolean touch(File file) { 743 if (file.exists()) { 744 file.setLastModified(System.currentTimeMillis()); 745 return true; 746 } 747 return false; 748 } 749 createJavaFile(File outFile)750 public static void createJavaFile(File outFile) throws IOException { 751 createJavaFile(outFile, null); 752 } 753 754 /** 755 * A method for creating a valid but very simple java file. 756 */ createJavaFile(File outFile, File superClass)757 public static void createJavaFile(File outFile, File superClass) 758 throws IOException { 759 String srcStr = "public class " + getSimpleName(outFile) + " "; 760 if (superClass != null) { 761 srcStr = srcStr.concat("extends " + getSimpleName(superClass) + " "); 762 } 763 srcStr = srcStr.concat("{}"); 764 try (PrintWriter ps = new PrintWriter(new FileWriter(outFile))) { 765 ps.println(srcStr); 766 } 767 } 768 769 /** 770 * Creates a java file name given its source. 771 * The file is created in the working directory, creating a directory 772 * tree if there is a package declaration. 773 */ createJavaFileFromSource(String source)774 public static void createJavaFileFromSource(String source) throws IOException { 775 createJavaFileFromSource(null, source); 776 } 777 778 /** 779 * Creates a java file name given its source. 780 * The file is created in the working directory, creating a directory 781 * tree if there is a package declaration or the argument initialPath 782 * has a valid path. 783 * 784 * e.i. if initialPath is foo/ and the source is: 785 * package bar; 786 * 787 * public class bazz {} 788 * 789 * this method will create the file foo/bar/bazz.java in the working 790 * directory. 791 */ createJavaFileFromSource(Path initialPath, String source)792 public static void createJavaFileFromSource(Path initialPath, 793 String source) throws IOException { 794 String fileName = getJavaFileNameFromSource(source); 795 String dirTree = getDirTreeFromSource(source); 796 Path path = (dirTree != null) ? 797 Paths.get(dirTree, fileName) : 798 Paths.get(fileName); 799 path = (initialPath != null) ? 800 initialPath.resolve(path): 801 path; 802 writeFile(path, source); 803 } 804 805 static Pattern publicClassPattern = 806 Pattern.compile("public\\s+(?:class|enum|interface){1}\\s+(\\w+)"); 807 static Pattern packageClassPattern = 808 Pattern.compile("(?:class|enum|interface){1}\\s+(\\w+)"); 809 810 /** 811 * Extracts the java file name from the class declaration. 812 * This method is intended for simple files and uses regular expressions, 813 * so comments matching the pattern can make the method fail. 814 */ getJavaFileNameFromSource(String source)815 private static String getJavaFileNameFromSource(String source) { 816 String className = null; 817 Matcher matcher = publicClassPattern.matcher(source); 818 if (matcher.find()) { 819 className = matcher.group(1) + ".java"; 820 } else { 821 matcher = packageClassPattern.matcher(source); 822 if (matcher.find()) { 823 className = matcher.group(1) + ".java"; 824 } else { 825 throw new AssertionError("Could not extract the java class " + 826 "name from the provided source"); 827 } 828 } 829 return className; 830 } 831 832 static Pattern packagePattern = 833 Pattern.compile("package\\s+(((?:\\w+\\.)*)(?:\\w+))"); 834 835 /** 836 * Extracts the path from the package declaration if present. 837 * This method is intended for simple files and uses regular expressions, 838 * so comments matching the pattern can make the method fail. 839 */ getDirTreeFromSource(String source)840 private static String getDirTreeFromSource(String source) { 841 Matcher matcher = packagePattern.matcher(source); 842 return matcher.find() ? 843 matcher.group(1).replace(".", File.separator) : 844 null; 845 } 846 847 /** 848 * A method for creating a jar's manifest file with supplied data. 849 */ mkManifestWithClassPath(String mainClass, String... classes)850 public static void mkManifestWithClassPath(String mainClass, 851 String... classes) throws IOException { 852 List <String> lines = new ArrayList<>(); 853 854 StringBuilder sb = new StringBuilder("Class-Path: ".length() + 855 classes[0].length()).append("Class-Path: ").append(classes[0]); 856 for (int i = 1; i < classes.length; i++) { 857 sb.append(" ").append(classes[i]); 858 } 859 lines.add(sb.toString()); 860 if (mainClass != null) { 861 lines.add(new StringBuilder("Main-Class: ".length() + 862 mainClass.length()) 863 .append("Main-Class: ") 864 .append(mainClass).toString()); 865 } 866 Files.write(Paths.get("MANIFEST.MF"), lines, null); 867 } 868 869 /** 870 * A utility method to obtain the file name. 871 */ getSimpleName(File inFile)872 static String getSimpleName(File inFile) { 873 return inFile.toPath().getFileName().toString(); 874 } 875 876 /** 877 * A method to write to a file, the directory tree is created if needed. 878 */ writeFile(Path path, String body)879 public static File writeFile(Path path, String body) throws IOException { 880 File result; 881 if (path.getParent() != null) { 882 Files.createDirectories(path.getParent()); 883 } 884 try (FileWriter out = new FileWriter(result = path.toAbsolutePath().toFile())) { 885 out.write(body); 886 } 887 return result; 888 } 889 writeFile(String path, String body)890 public static File writeFile(String path, String body) throws IOException { 891 return writeFile(Paths.get(path), body); 892 } 893 894 /** 895 * A rm-like method, the file is deleted only if it exists. 896 */ rm(Path path)897 public static void rm(Path path) throws Exception { 898 Files.deleteIfExists(path); 899 } 900 rm(String filename)901 public static void rm(String filename) throws Exception { 902 rm(Paths.get(filename)); 903 } 904 rm(File f)905 public static void rm(File f) throws Exception { 906 rm(f.toPath()); 907 } 908 909 /** 910 * Copy source file to destination file. 911 */ copyFile(File destfile, File srcfile)912 public static void copyFile(File destfile, File srcfile) 913 throws IOException { 914 copyFile(destfile.toPath(), srcfile.toPath()); 915 } 916 copyFile(Path destPath, Path srcPath)917 public static void copyFile(Path destPath, Path srcPath) 918 throws IOException { 919 Files.createDirectories(destPath); 920 Files.copy(srcPath, destPath, REPLACE_EXISTING); 921 } 922 923 /** 924 * Splits a String using the System's line separator character as splitting point. 925 */ splitLines(String lines, String sep)926 public static List<String> splitLines(String lines, String sep) { 927 return Arrays.asList(lines.split(sep)); 928 } 929 930 /** 931 * Converts a String list into one String by appending the System's line separator 932 * character after each component. 933 */ listToString(List<String> lines)934 private static String listToString(List<String> lines) { 935 StringBuilder sb = new StringBuilder(); 936 for (String s : lines) { 937 sb.append(s).append(lineSeparator); 938 } 939 return sb.toString(); 940 } 941 942 /** 943 * Returns true if the OS is a Windows version. 944 */ isWindows()945 public static boolean isWindows() { 946 String osName = System.getProperty("os.name"); 947 return osName.toUpperCase().startsWith("WINDOWS"); 948 } 949 950 /** 951 * Class representing an in-memory java source file. It is able to extract 952 * the file name from simple source codes using regular expressions. 953 */ 954 public static class JavaSource extends SimpleJavaFileObject { 955 String source; 956 String name; 957 JavaSource(String className, String source)958 public JavaSource(String className, String source) { 959 super(URI.create(className), 960 JavaFileObject.Kind.SOURCE); 961 this.name = className; 962 this.source = source; 963 } 964 JavaSource(String source)965 public JavaSource(String source) { 966 super(URI.create(getJavaFileNameFromSource(source)), 967 JavaFileObject.Kind.SOURCE); 968 this.name = getJavaFileNameFromSource(source); 969 this.source = source; 970 } 971 972 @Override getCharContent(boolean ignoreEncodingErrors)973 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 974 return source; 975 } 976 } 977 } 978