aoqi@0: /* aoqi@0: * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: import java.io.BufferedReader; aoqi@0: import java.io.File; aoqi@0: import java.io.FileNotFoundException; aoqi@0: import java.io.FileWriter; aoqi@0: import java.io.IOException; aoqi@0: import java.io.InputStreamReader; aoqi@0: import java.io.PrintWriter; aoqi@0: import java.io.StringWriter; aoqi@0: import java.net.URI; aoqi@0: import java.nio.charset.Charset; aoqi@0: import java.nio.file.Files; aoqi@0: import java.nio.file.Path; aoqi@0: import java.nio.file.Paths; aoqi@0: import java.nio.file.StandardOpenOption; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Arrays; aoqi@0: import java.util.Collection; aoqi@0: import java.util.Collections; aoqi@0: import java.util.EnumSet; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: import java.util.Set; aoqi@0: import java.util.regex.Matcher; aoqi@0: import java.util.regex.Pattern; aoqi@0: aoqi@0: import javax.tools.JavaCompiler; aoqi@0: import javax.tools.JavaFileObject; aoqi@0: import javax.tools.SimpleJavaFileObject; aoqi@0: import javax.tools.ToolProvider; aoqi@0: aoqi@0: import com.sun.source.util.JavacTask; aoqi@0: import com.sun.tools.javac.api.JavacTaskImpl; aoqi@0: aoqi@0: import sun.tools.jar.Main; aoqi@0: aoqi@0: import static java.nio.file.StandardCopyOption.*; aoqi@0: aoqi@0: /** aoqi@0: * Toolbox for jtreg tests. aoqi@0: */ aoqi@0: aoqi@0: public class ToolBox { aoqi@0: aoqi@0: public static final String lineSeparator = System.getProperty("line.separator"); aoqi@0: public static final String jdkUnderTest = System.getProperty("test.jdk"); aoqi@0: public static final Path javaBinary = Paths.get(jdkUnderTest, "bin", "java"); aoqi@0: public static final Path javacBinary = Paths.get(jdkUnderTest, "bin", "javac"); aoqi@0: aoqi@0: public static final List testToolVMOpts; aoqi@0: public static final List testVMOpts; aoqi@0: aoqi@0: private static final Charset defaultCharset = Charset.defaultCharset(); aoqi@0: aoqi@0: static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); aoqi@0: aoqi@0: static { aoqi@0: String sysProp = System.getProperty("test.tool.vm.opts"); aoqi@0: if (sysProp != null && sysProp.length() > 0) { aoqi@0: testToolVMOpts = Arrays.asList(sysProp.split("\\s+")); aoqi@0: } else { aoqi@0: testToolVMOpts = Collections.emptyList(); aoqi@0: } aoqi@0: aoqi@0: sysProp = System.getProperty("test.vm.opts"); aoqi@0: if (sysProp != null && sysProp.length() > 0) { aoqi@0: testVMOpts = Arrays.asList(sysProp.split("\\s+")); aoqi@0: } else { aoqi@0: testVMOpts = Collections.emptyList(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * The expected result of command-like method execution. aoqi@0: */ aoqi@0: public enum Expect {SUCCESS, FAIL} aoqi@0: aoqi@0: enum AcceptedParams { aoqi@0: EXPECT, aoqi@0: SOURCES, aoqi@0: OPTIONS, aoqi@0: STD_OUTPUT, aoqi@0: ERR_OUTPUT, aoqi@0: EXTRA_ENV, aoqi@0: } aoqi@0: aoqi@0: enum OutputKind {STD, ERR} aoqi@0: aoqi@0: /** aoqi@0: * Helper class to abstract the processing of command's output. aoqi@0: */ aoqi@0: static abstract class WriterHelper { aoqi@0: OutputKind kind; aoqi@0: public abstract void pipeOutput(ProcessBuilder pb); aoqi@0: public abstract void readFromStream(Process p) throws IOException; aoqi@0: public abstract void addAll(Collection c) throws IOException; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Helper class for redirecting command's output to a file. aoqi@0: */ aoqi@0: static class FileWriterHelper extends WriterHelper { aoqi@0: File file; aoqi@0: aoqi@0: FileWriterHelper(File file, OutputKind kind) { aoqi@0: this.file = file; aoqi@0: this.kind = kind; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void pipeOutput(ProcessBuilder pb) { aoqi@0: if (file != null) { aoqi@0: switch (kind) { aoqi@0: case STD: aoqi@0: pb.redirectInput(file); aoqi@0: break; aoqi@0: case ERR: aoqi@0: pb.redirectError(file); aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void readFromStream(Process p) throws IOException {} aoqi@0: aoqi@0: @Override aoqi@0: public void addAll(Collection c) throws IOException { aoqi@0: if (file.exists()) aoqi@0: Files.write(file.toPath(), c, defaultCharset, aoqi@0: StandardOpenOption.WRITE, StandardOpenOption.APPEND); aoqi@0: else aoqi@0: Files.write(file.toPath(), c, defaultCharset); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Helper class for redirecting command's output to a String list. aoqi@0: */ aoqi@0: static class ListWriterHelper extends WriterHelper { aoqi@0: List list; aoqi@0: aoqi@0: public ListWriterHelper(List list, OutputKind kind) { aoqi@0: this.kind = kind; aoqi@0: this.list = list; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void pipeOutput(ProcessBuilder pb) {} aoqi@0: aoqi@0: @Override aoqi@0: public void readFromStream(Process p) throws IOException { aoqi@0: BufferedReader br = null; aoqi@0: switch (kind) { aoqi@0: case STD: aoqi@0: br = new BufferedReader(new InputStreamReader(p.getInputStream())); aoqi@0: break; aoqi@0: case ERR: aoqi@0: br = new BufferedReader(new InputStreamReader(p.getErrorStream())); aoqi@0: break; aoqi@0: } aoqi@0: String line; aoqi@0: while ((line = br.readLine()) != null) { aoqi@0: list.add(line); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public void addAll(Collection c) { aoqi@0: list.addAll(c); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Simple factory class for creating a WriterHelper instance. aoqi@0: */ aoqi@0: static class WriterHelperFactory { aoqi@0: static WriterHelper make(File file, OutputKind kind) { aoqi@0: return new FileWriterHelper(file, kind); aoqi@0: } aoqi@0: aoqi@0: static WriterHelper make(List list, OutputKind kind) { aoqi@0: return new ListWriterHelper(list, kind); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A generic class for holding command's arguments. aoqi@0: */ aoqi@0: public static abstract class GenericArgs { aoqi@0: protected static List> minAcceptedParams; aoqi@0: aoqi@0: protected Set currentParams = aoqi@0: EnumSet.noneOf(AcceptedParams.class); aoqi@0: aoqi@0: protected Expect whatToExpect; aoqi@0: protected WriterHelper stdOutput; aoqi@0: protected WriterHelper errOutput; aoqi@0: protected List args = new ArrayList<>(); aoqi@0: protected String[] argsArr; aoqi@0: aoqi@0: protected GenericArgs() { aoqi@0: set(Expect.SUCCESS); aoqi@0: } aoqi@0: aoqi@0: public T set(Expect whatToExpt) { aoqi@0: currentParams.add(AcceptedParams.EXPECT); aoqi@0: this.whatToExpect = whatToExpt; aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T setStdOutput(List stdOutput) { aoqi@0: currentParams.add(AcceptedParams.STD_OUTPUT); aoqi@0: this.stdOutput = WriterHelperFactory.make(stdOutput, OutputKind.STD); aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T setStdOutput(File output) { aoqi@0: currentParams.add(AcceptedParams.STD_OUTPUT); aoqi@0: this.stdOutput = WriterHelperFactory.make(output, OutputKind.STD); aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T setErrOutput(List errOutput) { aoqi@0: currentParams.add(AcceptedParams.ERR_OUTPUT); aoqi@0: this.errOutput = WriterHelperFactory.make(errOutput, OutputKind.ERR); aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T setErrOutput(File errOutput) { aoqi@0: currentParams.add(AcceptedParams.ERR_OUTPUT); aoqi@0: this.errOutput = WriterHelperFactory.make(errOutput, OutputKind.ERR); aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T setAllArgs(String... args) { aoqi@0: currentParams.add(AcceptedParams.OPTIONS); aoqi@0: this.argsArr = args; aoqi@0: return (T) this; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: public T appendArgs(String... args) { aoqi@0: appendArgs(Arrays.asList(args)); aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T appendArgs(Path... args) { aoqi@0: if (args != null) { aoqi@0: List list = new ArrayList<>(); aoqi@0: for (int i = 0; i < args.length; i++) { aoqi@0: if (args[i] != null) { aoqi@0: list.add(args[i].toString()); aoqi@0: } aoqi@0: } aoqi@0: appendArgs(list); aoqi@0: } aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T appendArgs(List args) { aoqi@0: if (args != null && args.size() > 0) { aoqi@0: currentParams.add(AcceptedParams.OPTIONS); aoqi@0: for (int i = 0; i < args.size(); i++) { aoqi@0: if (args.get(i) != null) { aoqi@0: this.args.add(args.get(i)); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T setOptions(List options) { aoqi@0: currentParams.add(AcceptedParams.OPTIONS); aoqi@0: this.args = options; aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public T setOptions(String... options) { aoqi@0: currentParams.add(AcceptedParams.OPTIONS); aoqi@0: this.args = Arrays.asList(options); aoqi@0: return (T)this; aoqi@0: } aoqi@0: aoqi@0: public boolean hasMinParams() { aoqi@0: for (Set minSet : minAcceptedParams) { aoqi@0: if (currentParams.containsAll(minSet)) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A more specific class for holding javac-like command's arguments. aoqi@0: */ aoqi@0: public static class JavaToolArgs extends GenericArgs { aoqi@0: aoqi@0: static { aoqi@0: minAcceptedParams = new ArrayList<>(); aoqi@0: minAcceptedParams.add(EnumSet.of( aoqi@0: AcceptedParams.EXPECT, AcceptedParams.OPTIONS)); aoqi@0: minAcceptedParams.add(EnumSet.of( aoqi@0: AcceptedParams.EXPECT, AcceptedParams.SOURCES)); aoqi@0: } aoqi@0: aoqi@0: protected List sources; aoqi@0: aoqi@0: public JavaToolArgs() { aoqi@0: super(); aoqi@0: } aoqi@0: aoqi@0: public JavaToolArgs(Expect whatToExpt) { aoqi@0: super.set(whatToExpt); aoqi@0: } aoqi@0: aoqi@0: public JavaToolArgs setSources(List sources) { aoqi@0: currentParams.add(AcceptedParams.SOURCES); aoqi@0: this.sources = sources; aoqi@0: return this; aoqi@0: } aoqi@0: aoqi@0: public JavaToolArgs setSources(JavaSource... sources) { aoqi@0: return setSources(Arrays.asList(sources)); aoqi@0: } aoqi@0: aoqi@0: public JavaToolArgs setSources(String... sources) { aoqi@0: List javaSrcs = new ArrayList<>(); aoqi@0: for (String source : sources) { aoqi@0: javaSrcs.add(new JavaSource(source)); aoqi@0: } aoqi@0: return setSources(javaSrcs); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A more specific class for holding any command's arguments. aoqi@0: */ aoqi@0: public static class AnyToolArgs extends GenericArgs { aoqi@0: aoqi@0: static { aoqi@0: minAcceptedParams = new ArrayList<>(); aoqi@0: minAcceptedParams.add(EnumSet.of( aoqi@0: AcceptedParams.EXPECT, AcceptedParams.OPTIONS)); aoqi@0: } aoqi@0: aoqi@0: Map extraEnv; aoqi@0: aoqi@0: public AnyToolArgs() { aoqi@0: super(); aoqi@0: } aoqi@0: aoqi@0: public AnyToolArgs(Expect whatToExpt) { aoqi@0: set(whatToExpt); aoqi@0: } aoqi@0: aoqi@0: public AnyToolArgs set(Map extraEnv) { aoqi@0: currentParams.add(AcceptedParams.EXTRA_ENV); aoqi@0: this.extraEnv = extraEnv; aoqi@0: return this; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Custom exception for bad command execution. aoqi@0: */ aoqi@0: public static class CommandExecutionException extends Exception { aoqi@0: CommandExecutionException(List command, Expect whatToExpt) { aoqi@0: super(createMessage(command, whatToExpt)); aoqi@0: } aoqi@0: aoqi@0: CommandExecutionException(Expect whatToExpt, String... command) { aoqi@0: this(Arrays.asList(command), whatToExpt); aoqi@0: } aoqi@0: aoqi@0: private static String createMessage(List command, Expect whatToExpt) { aoqi@0: StringBuilder sb = new StringBuilder().append("Command : "); aoqi@0: sb.append(command.toString()).append(lineSeparator); aoqi@0: switch (whatToExpt) { aoqi@0: case SUCCESS: aoqi@0: sb.append(" has unexpectedly failed"); aoqi@0: break; aoqi@0: case FAIL: aoqi@0: sb.append(" has been unexpectedly successful"); aoqi@0: break; aoqi@0: } aoqi@0: return sb.toString(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Custom exception for not equal resources. aoqi@0: */ aoqi@0: public static class ResourcesNotEqualException extends Exception { aoqi@0: public ResourcesNotEqualException(List res1, List res2) { aoqi@0: super(createMessage(res1, res2)); aoqi@0: } aoqi@0: aoqi@0: public ResourcesNotEqualException(String line1, String line2) { aoqi@0: super(createMessage(line1, line2)); aoqi@0: } aoqi@0: aoqi@0: public ResourcesNotEqualException(Path path1, Path path2) { aoqi@0: super(createMessage(path1, path2)); aoqi@0: } aoqi@0: aoqi@0: private static String createMessage(Path path1, Path path2) { aoqi@0: return new StringBuilder() aoqi@0: .append("The resources provided for comparison in paths \n") aoqi@0: .append(path1.toString()).append(" and \n") aoqi@0: .append(path2.toString()).append("are different").toString(); aoqi@0: } aoqi@0: aoqi@0: private static String createMessage(String line1, String line2) { aoqi@0: return new StringBuilder() aoqi@0: .append("The resources provided for comparison are different at lines: \n") aoqi@0: .append(line1).append(" and \n") aoqi@0: .append(line2).toString(); aoqi@0: } aoqi@0: aoqi@0: private static String createMessage(List res1, List res2) { aoqi@0: return new StringBuilder() aoqi@0: .append("The resources provided for comparison are different: \n") aoqi@0: .append("Resource 1 is: ").append(res1).append("\n and \n") aoqi@0: .append("Resource 2 is: ").append(res2).append("\n").toString(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A javac compiler caller method. aoqi@0: */ aoqi@0: public static int javac(JavaToolArgs params) aoqi@0: throws CommandExecutionException, IOException { aoqi@0: if (params.hasMinParams()) { aoqi@0: if (params.argsArr != null) { aoqi@0: return genericJavaCMD(JavaCMD.JAVAC, params); aoqi@0: } else { aoqi@0: return genericJavaCMD(JavaCMD.JAVAC_API, params); aoqi@0: } aoqi@0: } aoqi@0: throw new AssertionError("javac command has been invoked with less parameters than needed"); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A javap calling method. aoqi@0: */ aoqi@0: public static String javap(JavaToolArgs params) aoqi@0: throws CommandExecutionException, IOException { aoqi@0: if (params.hasMinParams()) { aoqi@0: List list = new ArrayList<>(); aoqi@0: params.setErrOutput(list); aoqi@0: genericJavaCMD(JavaCMD.JAVAP, params); aoqi@0: return listToString(list); aoqi@0: } aoqi@0: throw new AssertionError("javap command has been invoked with less parameters than needed"); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A javah calling method. aoqi@0: */ aoqi@0: public static int javah(JavaToolArgs params) aoqi@0: throws CommandExecutionException, IOException { aoqi@0: if (params.hasMinParams()) { aoqi@0: return genericJavaCMD(JavaCMD.JAVAH, params); aoqi@0: } aoqi@0: throw new AssertionError("javah command has been invoked with less parameters than needed"); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A enum class for langtools commands. aoqi@0: */ aoqi@0: enum JavaCMD { aoqi@0: JAVAC { aoqi@0: @Override aoqi@0: int run(JavaToolArgs params, PrintWriter pw) { aoqi@0: return com.sun.tools.javac.Main.compile(params.argsArr, pw); aoqi@0: } aoqi@0: }, aoqi@0: JAVAC_API { aoqi@0: @Override aoqi@0: int run(JavaToolArgs params, PrintWriter pw) { aoqi@0: JavacTask ct = (JavacTask)comp.getTask(pw, null, null, aoqi@0: params.args, null, params.sources); aoqi@0: return ((JavacTaskImpl)ct).doCall().exitCode; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: String getName() { aoqi@0: return "javac"; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: List getExceptionMsgContent(JavaToolArgs params) { aoqi@0: List result = super.getExceptionMsgContent(params); aoqi@0: for (JavaFileObject source : params.sources) { aoqi@0: if (source instanceof JavaSource) { aoqi@0: result.add(((JavaSource)source).name); aoqi@0: } aoqi@0: } aoqi@0: return result; aoqi@0: } aoqi@0: }, aoqi@0: JAVAH { aoqi@0: @Override aoqi@0: int run(JavaToolArgs params, PrintWriter pw) { aoqi@0: return com.sun.tools.javah.Main.run(params.argsArr, pw); aoqi@0: } aoqi@0: }, aoqi@0: JAVAP { aoqi@0: @Override aoqi@0: int run(JavaToolArgs params, PrintWriter pw) { aoqi@0: return com.sun.tools.javap.Main.run(params.argsArr, pw); aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: abstract int run(JavaToolArgs params, PrintWriter pw); aoqi@0: aoqi@0: String getName() { aoqi@0: return this.name().toLowerCase(); aoqi@0: } aoqi@0: aoqi@0: List getExceptionMsgContent(JavaToolArgs params) { aoqi@0: List result = new ArrayList<>(); aoqi@0: result.add(getName()); aoqi@0: result.addAll(params.argsArr != null ? aoqi@0: Arrays.asList(params.argsArr) : aoqi@0: params.args); aoqi@0: return result; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A helper method for executing langtools commands. aoqi@0: */ aoqi@0: private static int genericJavaCMD( aoqi@0: JavaCMD cmd, aoqi@0: JavaToolArgs params) aoqi@0: throws CommandExecutionException, IOException { aoqi@0: int rc = 0; aoqi@0: StringWriter sw = null; aoqi@0: try (PrintWriter pw = (params.errOutput == null) ? aoqi@0: null : new PrintWriter(sw = new StringWriter())) { aoqi@0: rc = cmd.run(params, pw); aoqi@0: } aoqi@0: String out = (sw == null) ? null : sw.toString(); aoqi@0: aoqi@0: if (params.errOutput != null && (out != null) && !out.isEmpty()) { aoqi@0: params.errOutput.addAll(splitLines(out, lineSeparator)); aoqi@0: } aoqi@0: aoqi@0: if ( (rc == 0 && params.whatToExpect == Expect.SUCCESS) || aoqi@0: (rc != 0 && params.whatToExpect == Expect.FAIL) ) { aoqi@0: return rc; aoqi@0: } aoqi@0: aoqi@0: throw new CommandExecutionException(cmd.getExceptionMsgContent(params), aoqi@0: params.whatToExpect); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A jar calling method. aoqi@0: */ aoqi@0: public static boolean jar(String... params) throws CommandExecutionException { aoqi@0: Main jarGenerator = new Main(System.out, System.err, "jar"); aoqi@0: boolean result = jarGenerator.run(params); aoqi@0: if (!result) { aoqi@0: List command = new ArrayList<>(); aoqi@0: command.add("jar"); aoqi@0: command.addAll(Arrays.asList(params)); aoqi@0: throw new CommandExecutionException(command, Expect.SUCCESS); aoqi@0: } aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A general command calling method. aoqi@0: */ aoqi@0: public static int executeCommand(AnyToolArgs params) aoqi@0: throws CommandExecutionException, IOException, InterruptedException { aoqi@0: if (params.hasMinParams()) { aoqi@0: List cmd = (params.args != null) ? aoqi@0: params.args : aoqi@0: Arrays.asList(params.argsArr); aoqi@0: return executeCommand(cmd, params.extraEnv, params.stdOutput, aoqi@0: params.errOutput, params.whatToExpect); aoqi@0: } aoqi@0: throw new AssertionError("command has been invoked with less parameters than needed"); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A helper method for calling a general command. aoqi@0: */ aoqi@0: private static int executeCommand( aoqi@0: List command, aoqi@0: Map extraEnv, aoqi@0: WriterHelper stdOutput, aoqi@0: WriterHelper errOutput, aoqi@0: Expect whatToExpt) aoqi@0: throws IOException, InterruptedException, CommandExecutionException { aoqi@0: ProcessBuilder pb = new ProcessBuilder(command); aoqi@0: aoqi@0: if (stdOutput != null) stdOutput.pipeOutput(pb); aoqi@0: if (errOutput != null) errOutput.pipeOutput(pb); aoqi@0: aoqi@0: if (extraEnv != null) { aoqi@0: pb.environment().putAll(extraEnv); aoqi@0: } aoqi@0: aoqi@0: Process p = pb.start(); aoqi@0: aoqi@0: if (stdOutput != null) stdOutput.readFromStream(p); aoqi@0: if (errOutput != null) errOutput.readFromStream(p); aoqi@0: aoqi@0: int result = p.waitFor(); aoqi@0: if ( (result == 0 && whatToExpt == Expect.SUCCESS) || aoqi@0: (result != 0 && whatToExpt == Expect.FAIL) ) { aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: throw new CommandExecutionException(command, whatToExpt); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * This set of methods can be used instead of diff when the only needed aoqi@0: * result is the equality or inequality of the two given resources. aoqi@0: * aoqi@0: * A resource can be a file or a String list. aoqi@0: */ aoqi@0: public static void compareLines(Path aPath, Path otherPath, String encoding) aoqi@0: throws FileNotFoundException, IOException, ResourcesNotEqualException { aoqi@0: compareLines(aPath, otherPath, encoding, false); aoqi@0: } aoqi@0: aoqi@0: public static void compareLines( aoqi@0: Path aPath, Path otherPath, String encoding, boolean trim) aoqi@0: throws FileNotFoundException, IOException, ResourcesNotEqualException { aoqi@0: Charset charset = encoding != null ? aoqi@0: Charset.forName(encoding) : aoqi@0: defaultCharset; aoqi@0: List list1 = Files.readAllLines(aPath, charset); aoqi@0: List list2 = Files.readAllLines(otherPath, charset); aoqi@0: compareLines(list1, list2, trim); aoqi@0: } aoqi@0: aoqi@0: public static void compareLines(Path path, List strings, String encoding) aoqi@0: throws FileNotFoundException, IOException, ResourcesNotEqualException { aoqi@0: compareLines(path, strings, encoding, false); aoqi@0: } aoqi@0: aoqi@0: public static void compareLines(Path path, List strings, aoqi@0: String encoding, boolean trim) aoqi@0: throws FileNotFoundException, IOException, ResourcesNotEqualException { aoqi@0: Charset charset = encoding != null ? aoqi@0: Charset.forName(encoding) : aoqi@0: defaultCharset; aoqi@0: List list = Files.readAllLines(path, charset); aoqi@0: compareLines(list, strings, trim); aoqi@0: } aoqi@0: aoqi@0: public static void compareLines(List list1, List list2) aoqi@0: throws ResourcesNotEqualException { aoqi@0: compareLines(list1, list2, false); aoqi@0: } aoqi@0: aoqi@0: public static void compareLines(List list1, aoqi@0: List list2, boolean trim) throws ResourcesNotEqualException { aoqi@0: if ((list1 == list2) || (list1 == null && list2 == null)) return; aoqi@0: if (list1.size() != list2.size()) aoqi@0: throw new ResourcesNotEqualException(list1, list2); aoqi@0: int i = 0; aoqi@0: int j = 0; aoqi@0: while (i < list1.size() && aoqi@0: j < list2.size() && aoqi@0: equals(list1.get(i), list2.get(j), trim)) { aoqi@0: i++; j++; aoqi@0: } aoqi@0: if (!(i == list1.size() && j == list2.size())) aoqi@0: throw new ResourcesNotEqualException(list1, list2); aoqi@0: } aoqi@0: aoqi@0: private static boolean equals(String s1, String s2, boolean trim) { aoqi@0: return (trim ? s1.trim().equals(s2.trim()) : s1.equals(s2)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A set of simple grep-like methods, looks for regExpr in text. aoqi@0: * The content of text is split using the new line character as a pattern aoqi@0: * and later the regExpr is seek in every split line. If a match is found, aoqi@0: * the whole line is added to the result. aoqi@0: */ aoqi@0: public static List grep(String regExpr, String text, String sep) { aoqi@0: return grep(regExpr, splitLines(text, sep)); aoqi@0: } aoqi@0: aoqi@0: public static List grep(String regExpr, List text) { aoqi@0: List result = new ArrayList<>(); aoqi@0: Pattern pattern = Pattern.compile(regExpr); aoqi@0: for (String s : text) { aoqi@0: if (pattern.matcher(s).find()) { aoqi@0: result.add(s); aoqi@0: } aoqi@0: } aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: public static List grep(String regExpr, File f) aoqi@0: throws IOException { aoqi@0: List lines = Files.readAllLines(f.toPath(), defaultCharset); aoqi@0: return grep(regExpr, lines); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A touch-like method. aoqi@0: */ aoqi@0: public static boolean touch(String fileName) { aoqi@0: File file = new File(fileName); aoqi@0: return touch(file); aoqi@0: } aoqi@0: aoqi@0: public static boolean touch(File file) { aoqi@0: if (file.exists()) { aoqi@0: file.setLastModified(System.currentTimeMillis()); aoqi@0: return true; aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public static void createJavaFile(File outFile) throws IOException { aoqi@0: createJavaFile(outFile, null); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A method for creating a valid but very simple java file. aoqi@0: */ aoqi@0: public static void createJavaFile(File outFile, File superClass) aoqi@0: throws IOException { aoqi@0: String srcStr = "public class " + getSimpleName(outFile) + " "; aoqi@0: if (superClass != null) { aoqi@0: srcStr = srcStr.concat("extends " + getSimpleName(superClass) + " "); aoqi@0: } aoqi@0: srcStr = srcStr.concat("{}"); aoqi@0: try (PrintWriter ps = new PrintWriter(new FileWriter(outFile))) { aoqi@0: ps.println(srcStr); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Creates a java file name given its source. aoqi@0: * The file is created in the working directory, creating a directory aoqi@0: * tree if there is a package declaration. aoqi@0: */ aoqi@0: public static void createJavaFileFromSource(String source) throws IOException { aoqi@0: createJavaFileFromSource(null, source); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Creates a java file name given its source. aoqi@0: * The file is created in the working directory, creating a directory aoqi@0: * tree if there is a package declaration or the argument initialPath aoqi@0: * has a valid path. aoqi@0: * aoqi@0: * e.i. if initialPath is foo/ and the source is: aoqi@0: * package bar; aoqi@0: * aoqi@0: * public class bazz {} aoqi@0: * aoqi@0: * this method will create the file foo/bar/bazz.java in the working aoqi@0: * directory. aoqi@0: */ aoqi@0: public static void createJavaFileFromSource(Path initialPath, aoqi@0: String source) throws IOException { aoqi@0: String fileName = getJavaFileNameFromSource(source); aoqi@0: String dirTree = getDirTreeFromSource(source); aoqi@0: Path path = (dirTree != null) ? aoqi@0: Paths.get(dirTree, fileName) : aoqi@0: Paths.get(fileName); aoqi@0: path = (initialPath != null) ? aoqi@0: initialPath.resolve(path): aoqi@0: path; aoqi@0: writeFile(path, source); aoqi@0: } aoqi@0: aoqi@0: static Pattern publicClassPattern = aoqi@0: Pattern.compile("public\\s+(?:class|enum|interface){1}\\s+(\\w+)"); aoqi@0: static Pattern packageClassPattern = aoqi@0: Pattern.compile("(?:class|enum|interface){1}\\s+(\\w+)"); aoqi@0: aoqi@0: /** aoqi@0: * Extracts the java file name from the class declaration. aoqi@0: * This method is intended for simple files and uses regular expressions, aoqi@0: * so comments matching the pattern can make the method fail. aoqi@0: */ aoqi@0: private static String getJavaFileNameFromSource(String source) { aoqi@0: String className = null; aoqi@0: Matcher matcher = publicClassPattern.matcher(source); aoqi@0: if (matcher.find()) { aoqi@0: className = matcher.group(1) + ".java"; aoqi@0: } else { aoqi@0: matcher = packageClassPattern.matcher(source); aoqi@0: if (matcher.find()) { aoqi@0: className = matcher.group(1) + ".java"; aoqi@0: } else { aoqi@0: throw new AssertionError("Could not extract the java class " + aoqi@0: "name from the provided source"); aoqi@0: } aoqi@0: } aoqi@0: return className; aoqi@0: } aoqi@0: aoqi@0: static Pattern packagePattern = aoqi@0: Pattern.compile("package\\s+(((?:\\w+\\.)*)(?:\\w+))"); aoqi@0: aoqi@0: /** aoqi@0: * Extracts the path from the package declaration if present. aoqi@0: * This method is intended for simple files and uses regular expressions, aoqi@0: * so comments matching the pattern can make the method fail. aoqi@0: */ aoqi@0: private static String getDirTreeFromSource(String source) { aoqi@0: Matcher matcher = packagePattern.matcher(source); aoqi@0: return matcher.find() ? aoqi@0: matcher.group(1).replace(".", File.separator) : aoqi@0: null; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A method for creating a jar's manifest file with supplied data. aoqi@0: */ aoqi@0: public static void mkManifestWithClassPath(String mainClass, aoqi@0: String... classes) throws IOException { aoqi@0: List lines = new ArrayList<>(); aoqi@0: aoqi@0: StringBuilder sb = new StringBuilder("Class-Path: ".length() + aoqi@0: classes[0].length()).append("Class-Path: ").append(classes[0]); aoqi@0: for (int i = 1; i < classes.length; i++) { aoqi@0: sb.append(" ").append(classes[i]); aoqi@0: } aoqi@0: lines.add(sb.toString()); aoqi@0: if (mainClass != null) { aoqi@0: lines.add(new StringBuilder("Main-Class: ".length() + aoqi@0: mainClass.length()) aoqi@0: .append("Main-Class: ") aoqi@0: .append(mainClass).toString()); aoqi@0: } aoqi@0: Files.write(Paths.get("MANIFEST.MF"), lines, null); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A utility method to obtain the file name. aoqi@0: */ aoqi@0: static String getSimpleName(File inFile) { aoqi@0: return inFile.toPath().getFileName().toString(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A method to write to a file, the directory tree is created if needed. aoqi@0: */ aoqi@0: public static File writeFile(Path path, String body) throws IOException { aoqi@0: File result; aoqi@0: if (path.getParent() != null) { aoqi@0: Files.createDirectories(path.getParent()); aoqi@0: } aoqi@0: try (FileWriter out = new FileWriter(result = path.toAbsolutePath().toFile())) { aoqi@0: out.write(body); aoqi@0: } aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: public static File writeFile(String path, String body) throws IOException { aoqi@0: return writeFile(Paths.get(path), body); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A rm-like method, the file is deleted only if it exists. aoqi@0: */ aoqi@0: public static void rm(Path path) throws Exception { aoqi@0: Files.deleteIfExists(path); aoqi@0: } aoqi@0: aoqi@0: public static void rm(String filename) throws Exception { aoqi@0: rm(Paths.get(filename)); aoqi@0: } aoqi@0: aoqi@0: public static void rm(File f) throws Exception { aoqi@0: rm(f.toPath()); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Copy source file to destination file. aoqi@0: */ aoqi@0: public static void copyFile(File destfile, File srcfile) aoqi@0: throws IOException { aoqi@0: copyFile(destfile.toPath(), srcfile.toPath()); aoqi@0: } aoqi@0: aoqi@0: public static void copyFile(Path destPath, Path srcPath) aoqi@0: throws IOException { aoqi@0: Files.createDirectories(destPath); aoqi@0: Files.copy(srcPath, destPath, REPLACE_EXISTING); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Splits a String using the System's line separator character as splitting point. aoqi@0: */ aoqi@0: public static List splitLines(String lines, String sep) { aoqi@0: return Arrays.asList(lines.split(sep)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Converts a String list into one String by appending the System's line separator aoqi@0: * character after each component. aoqi@0: */ aoqi@0: private static String listToString(List lines) { aoqi@0: StringBuilder sb = new StringBuilder(); aoqi@0: for (String s : lines) { aoqi@0: sb.append(s).append(lineSeparator); aoqi@0: } aoqi@0: return sb.toString(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns true if the OS is a Windows version. aoqi@0: */ aoqi@0: public static boolean isWindows() { aoqi@0: String osName = System.getProperty("os.name"); aoqi@0: return osName.toUpperCase().startsWith("WINDOWS"); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Class representing an in-memory java source file. It is able to extract aoqi@0: * the file name from simple source codes using regular expressions. aoqi@0: */ aoqi@0: public static class JavaSource extends SimpleJavaFileObject { aoqi@0: String source; aoqi@0: String name; aoqi@0: aoqi@0: public JavaSource(String className, String source) { aoqi@0: super(URI.create(className), aoqi@0: JavaFileObject.Kind.SOURCE); aoqi@0: this.name = className; aoqi@0: this.source = source; aoqi@0: } aoqi@0: aoqi@0: public JavaSource(String source) { aoqi@0: super(URI.create(getJavaFileNameFromSource(source)), aoqi@0: JavaFileObject.Kind.SOURCE); aoqi@0: this.name = getJavaFileNameFromSource(source); aoqi@0: this.source = source; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public CharSequence getCharContent(boolean ignoreEncodingErrors) { aoqi@0: return source; aoqi@0: } aoqi@0: } aoqi@0: }