langtools/test/tools/javac/lib/ToolBox.java
changeset 26228 317aad3480c8
parent 26227 aa7ada31eb77
parent 26188 291708c85e76
child 26229 b1d885c12096
equal deleted inserted replaced
26227:aa7ada31eb77 26228:317aad3480c8
     1 /*
       
     2  * Copyright (c) 2013, 2014, 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.ByteArrayOutputStream;
       
    26 import java.io.File;
       
    27 import java.io.FileNotFoundException;
       
    28 import java.io.FileWriter;
       
    29 import java.io.FilterOutputStream;
       
    30 import java.io.IOException;
       
    31 import java.io.InputStreamReader;
       
    32 import java.io.OutputStream;
       
    33 import java.io.PrintWriter;
       
    34 import java.io.StringWriter;
       
    35 import java.net.URI;
       
    36 import java.nio.charset.Charset;
       
    37 import java.nio.file.Files;
       
    38 import java.nio.file.Path;
       
    39 import java.nio.file.Paths;
       
    40 import java.nio.file.StandardOpenOption;
       
    41 import java.util.ArrayList;
       
    42 import java.util.Arrays;
       
    43 import java.util.Collection;
       
    44 import java.util.Collections;
       
    45 import java.util.EnumSet;
       
    46 import java.util.HashMap;
       
    47 import java.util.List;
       
    48 import java.util.Map;
       
    49 import java.util.Set;
       
    50 import java.util.regex.Matcher;
       
    51 import java.util.regex.Pattern;
       
    52 
       
    53 import javax.tools.FileObject;
       
    54 import javax.tools.ForwardingJavaFileManager;
       
    55 import javax.tools.JavaCompiler;
       
    56 import javax.tools.JavaFileManager;
       
    57 import javax.tools.JavaFileManager.Location;
       
    58 import javax.tools.JavaFileObject;
       
    59 import javax.tools.JavaFileObject.Kind;
       
    60 import javax.tools.SimpleJavaFileObject;
       
    61 import javax.tools.StandardJavaFileManager;
       
    62 import javax.tools.ToolProvider;
       
    63 
       
    64 import com.sun.source.util.JavacTask;
       
    65 import com.sun.tools.javac.api.JavacTaskImpl;
       
    66 
       
    67 import sun.tools.jar.Main;
       
    68 
       
    69 import static java.nio.file.StandardCopyOption.*;
       
    70 
       
    71 /**
       
    72  * Toolbox for jtreg tests.
       
    73  */
       
    74 
       
    75 public class ToolBox {
       
    76 
       
    77     public static final String lineSeparator = System.getProperty("line.separator");
       
    78     public static final String jdkUnderTest = System.getProperty("test.jdk");
       
    79     public static final Path javaBinary = Paths.get(jdkUnderTest, "bin", "java");
       
    80     public static final Path javacBinary = Paths.get(jdkUnderTest, "bin", "javac");
       
    81 
       
    82     public static final List<String> testVMOpts = readOptions("test.vm.opts");
       
    83     public static final List<String> testToolVMOpts = readOptions("test.tool.vm.opts");
       
    84     public static final List<String> testJavaOpts = readOptions("test.java.opts");
       
    85 
       
    86     private static final Charset defaultCharset = Charset.defaultCharset();
       
    87 
       
    88     static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
       
    89 
       
    90     private static List<String> readOptions(String property) {
       
    91         String options = System.getProperty(property, "");
       
    92         return options.length() > 0 ? Arrays.asList(options.split("\\s+")) : Collections.<String>emptyList();
       
    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;
       
   116         public abstract void pipeOutput(ProcessBuilder pb);
       
   117         public abstract void readFromStream(Process p) throws IOException;
       
   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 
       
   127         FileWriterHelper(File file, OutputKind kind) {
       
   128             this.file = file;
       
   129             this.kind = kind;
       
   130         }
       
   131 
       
   132         @Override
       
   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
       
   147         public void readFromStream(Process p) throws IOException {}
       
   148 
       
   149         @Override
       
   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 
       
   165         public ListWriterHelper(List<String> list, OutputKind kind) {
       
   166             this.kind = kind;
       
   167             this.list = list;
       
   168         }
       
   169 
       
   170         @Override
       
   171         public void pipeOutput(ProcessBuilder pb) {}
       
   172 
       
   173         @Override
       
   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 
       
   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 {
       
   199         static WriterHelper make(File file, OutputKind kind) {
       
   200             return new FileWriterHelper(file, kind);
       
   201         }
       
   202 
       
   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 
       
   223         protected GenericArgs() {
       
   224             set(Expect.SUCCESS);
       
   225         }
       
   226 
       
   227         public T set(Expect whatToExpt) {
       
   228             currentParams.add(AcceptedParams.EXPECT);
       
   229             this.whatToExpect = whatToExpt;
       
   230             return (T)this;
       
   231         }
       
   232 
       
   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 
       
   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 
       
   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 
       
   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 
       
   257         public T setAllArgs(String... args) {
       
   258             currentParams.add(AcceptedParams.OPTIONS);
       
   259             this.argsArr = args;
       
   260             return (T) this;
       
   261         }
       
   262 
       
   263 
       
   264         public T appendArgs(String... args) {
       
   265             appendArgs(Arrays.asList(args));
       
   266             return (T)this;
       
   267         }
       
   268 
       
   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 
       
   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 
       
   294         public T setOptions(List<String> options) {
       
   295             currentParams.add(AcceptedParams.OPTIONS);
       
   296             this.args = options;
       
   297             return (T)this;
       
   298         }
       
   299 
       
   300         public T setOptions(String... options) {
       
   301             currentParams.add(AcceptedParams.OPTIONS);
       
   302             this.args = Arrays.asList(options);
       
   303             return (T)this;
       
   304         }
       
   305 
       
   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<>();
       
   323             minAcceptedParams.add(EnumSet.<AcceptedParams>of(
       
   324                     AcceptedParams.EXPECT, AcceptedParams.OPTIONS));
       
   325             minAcceptedParams.add(EnumSet.<AcceptedParams>of(
       
   326                     AcceptedParams.EXPECT, AcceptedParams.SOURCES));
       
   327         }
       
   328 
       
   329         protected List<? extends JavaFileObject> sources;
       
   330 
       
   331         public JavaToolArgs() {
       
   332             super();
       
   333         }
       
   334 
       
   335         public JavaToolArgs(Expect whatToExpt) {
       
   336             super.set(whatToExpt);
       
   337         }
       
   338 
       
   339         public JavaToolArgs setSources(List<? extends JavaFileObject> sources) {
       
   340             currentParams.add(AcceptedParams.SOURCES);
       
   341             this.sources = sources;
       
   342             return this;
       
   343         }
       
   344 
       
   345         public JavaToolArgs setSources(JavaSource... sources) {
       
   346             return setSources(Arrays.asList(sources));
       
   347         }
       
   348 
       
   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<>();
       
   365             minAcceptedParams.add(EnumSet.<AcceptedParams>of(
       
   366                     AcceptedParams.EXPECT, AcceptedParams.OPTIONS));
       
   367         }
       
   368 
       
   369         Map<String, String> extraEnv;
       
   370 
       
   371         public AnyToolArgs() {
       
   372             super();
       
   373         }
       
   374 
       
   375         public AnyToolArgs(Expect whatToExpt) {
       
   376             set(whatToExpt);
       
   377         }
       
   378 
       
   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 {
       
   390         CommandExecutionException(List<String> command, Expect whatToExpt) {
       
   391             super(createMessage(command, whatToExpt));
       
   392         }
       
   393 
       
   394         CommandExecutionException(Expect whatToExpt, String... command) {
       
   395             this(Arrays.asList(command), whatToExpt);
       
   396         }
       
   397 
       
   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 {
       
   417         public ResourcesNotEqualException(List<String> res1, List<String> res2) {
       
   418             super(createMessage(res1, res2));
       
   419         }
       
   420 
       
   421         public ResourcesNotEqualException(String line1, String line2) {
       
   422             super(createMessage(line1, line2));
       
   423         }
       
   424 
       
   425         public ResourcesNotEqualException(Path path1, Path path2) {
       
   426             super(createMessage(path1, path2));
       
   427         }
       
   428 
       
   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 
       
   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 
       
   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      */
       
   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      * Run javac and return the resulting classfiles.
       
   468      */
       
   469     public static Map<String, byte[]> compile(JavaToolArgs params)
       
   470             throws CommandExecutionException, IOException {
       
   471         if (params.hasMinParams()) {
       
   472             if (params.argsArr != null) {
       
   473                 throw new AssertionError("setAllArgs is not supported for compile");
       
   474             }
       
   475 
       
   476             StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
       
   477             MemoryFileManager mfm = new MemoryFileManager(fm);
       
   478             StringWriter sw = null;
       
   479             boolean rc;
       
   480 
       
   481             try (PrintWriter pw = (params.errOutput == null) ?
       
   482                     null : new PrintWriter(sw = new StringWriter())) {
       
   483                 JavacTask ct = (JavacTask)comp.getTask(pw, mfm, null,
       
   484                         params.args, null, params.sources);
       
   485                 rc = ct.call();
       
   486             }
       
   487 
       
   488             String out = (sw == null) ? null : sw.toString();
       
   489 
       
   490             if (params.errOutput != null && (out != null) && !out.isEmpty()) {
       
   491                 params.errOutput.addAll(splitLines(out, lineSeparator));
       
   492             }
       
   493 
       
   494             if ( ( rc && params.whatToExpect == Expect.SUCCESS) ||
       
   495                  (!rc && params.whatToExpect == Expect.FAIL) ) {
       
   496                 return mfm.classes;
       
   497             }
       
   498 
       
   499             throw new CommandExecutionException(JavaCMD.JAVAC_API.getExceptionMsgContent(params),
       
   500                     params.whatToExpect);
       
   501         }
       
   502         throw new AssertionError("compile command has been invoked with less parameters than needed");
       
   503     }
       
   504 
       
   505     /**
       
   506      * A javap calling method.
       
   507      */
       
   508     public static String javap(JavaToolArgs params)
       
   509             throws CommandExecutionException, IOException {
       
   510         if (params.hasMinParams()) {
       
   511             List<String> list = new ArrayList<>();
       
   512             params.setErrOutput(list);
       
   513             genericJavaCMD(JavaCMD.JAVAP, params);
       
   514             return listToString(list);
       
   515         }
       
   516         throw new AssertionError("javap command has been invoked with less parameters than needed");
       
   517     }
       
   518 
       
   519     /**
       
   520      * A javah calling method.
       
   521      */
       
   522     public static int javah(JavaToolArgs params)
       
   523             throws CommandExecutionException, IOException {
       
   524         if (params.hasMinParams()) {
       
   525             return genericJavaCMD(JavaCMD.JAVAH, params);
       
   526         }
       
   527         throw new AssertionError("javah command has been invoked with less parameters than needed");
       
   528     }
       
   529 
       
   530     /**
       
   531      * A enum class for langtools commands.
       
   532      */
       
   533     enum JavaCMD {
       
   534         JAVAC {
       
   535             @Override
       
   536             int run(JavaToolArgs params, PrintWriter pw) {
       
   537                 return com.sun.tools.javac.Main.compile(params.argsArr, pw);
       
   538             }
       
   539         },
       
   540         JAVAC_API {
       
   541             @Override
       
   542             int run(JavaToolArgs params, PrintWriter pw) {
       
   543                 JavacTask ct = (JavacTask)comp.getTask(pw, null, null,
       
   544                         params.args, null, params.sources);
       
   545                 return ((JavacTaskImpl)ct).doCall().exitCode;
       
   546             }
       
   547 
       
   548             @Override
       
   549             String getName() {
       
   550                 return "javac";
       
   551             }
       
   552 
       
   553             @Override
       
   554             List<String> getExceptionMsgContent(JavaToolArgs params) {
       
   555                 List<String> result = super.getExceptionMsgContent(params);
       
   556                 for (JavaFileObject source : params.sources) {
       
   557                     if (source instanceof JavaSource) {
       
   558                         result.add(((JavaSource)source).name);
       
   559                     }
       
   560                 }
       
   561                 return result;
       
   562             }
       
   563         },
       
   564         JAVAH {
       
   565             @Override
       
   566             int run(JavaToolArgs params, PrintWriter pw) {
       
   567                 return com.sun.tools.javah.Main.run(params.argsArr, pw);
       
   568             }
       
   569         },
       
   570         JAVAP {
       
   571             @Override
       
   572             int run(JavaToolArgs params, PrintWriter pw) {
       
   573                 return com.sun.tools.javap.Main.run(params.argsArr, pw);
       
   574             }
       
   575         };
       
   576 
       
   577         abstract int run(JavaToolArgs params, PrintWriter pw);
       
   578 
       
   579         String getName() {
       
   580             return this.name().toLowerCase();
       
   581         }
       
   582 
       
   583         List<String> getExceptionMsgContent(JavaToolArgs params) {
       
   584             List<String> result = new ArrayList<>();
       
   585             result.add(getName());
       
   586             result.addAll(params.argsArr != null ?
       
   587                     Arrays.asList(params.argsArr) :
       
   588                     params.args);
       
   589             return result;
       
   590         }
       
   591     }
       
   592 
       
   593     /**
       
   594      * A helper method for executing langtools commands.
       
   595      */
       
   596     private static int genericJavaCMD(
       
   597             JavaCMD cmd,
       
   598             JavaToolArgs params)
       
   599             throws CommandExecutionException, IOException {
       
   600         int rc = 0;
       
   601         StringWriter sw = null;
       
   602         try (PrintWriter pw = (params.errOutput == null) ?
       
   603                 null : new PrintWriter(sw = new StringWriter())) {
       
   604             rc = cmd.run(params, pw);
       
   605         }
       
   606         String out = (sw == null) ? null : sw.toString();
       
   607 
       
   608         if (params.errOutput != null && (out != null) && !out.isEmpty()) {
       
   609             params.errOutput.addAll(splitLines(out, lineSeparator));
       
   610         }
       
   611 
       
   612         if ( (rc == 0 && params.whatToExpect == Expect.SUCCESS) ||
       
   613              (rc != 0 && params.whatToExpect == Expect.FAIL) ) {
       
   614             return rc;
       
   615         }
       
   616 
       
   617         throw new CommandExecutionException(cmd.getExceptionMsgContent(params),
       
   618                 params.whatToExpect);
       
   619     }
       
   620 
       
   621     /**
       
   622      * A jar calling method.
       
   623      */
       
   624     public static boolean jar(String... params) throws CommandExecutionException {
       
   625         Main jarGenerator = new Main(System.out, System.err, "jar");
       
   626         boolean result = jarGenerator.run(params);
       
   627         if (!result) {
       
   628             List<String> command = new ArrayList<>();
       
   629             command.add("jar");
       
   630             command.addAll(Arrays.asList(params));
       
   631             throw new CommandExecutionException(command, Expect.SUCCESS);
       
   632         }
       
   633         return result;
       
   634     }
       
   635 
       
   636     /**
       
   637      * A general command calling method.
       
   638      */
       
   639     public static int executeCommand(AnyToolArgs params)
       
   640             throws CommandExecutionException, IOException, InterruptedException {
       
   641         if (params.hasMinParams()) {
       
   642             List<String> cmd = (params.args != null) ?
       
   643                     params.args :
       
   644                     Arrays.asList(params.argsArr);
       
   645             return executeCommand(cmd, params.extraEnv, params.stdOutput,
       
   646                     params.errOutput, params.whatToExpect);
       
   647         }
       
   648         throw new AssertionError("command has been invoked with less parameters than needed");
       
   649     }
       
   650 
       
   651     /**
       
   652      * A helper method for calling a general command.
       
   653      */
       
   654     private static int executeCommand(
       
   655             List<String> command,
       
   656             Map<String, String> extraEnv,
       
   657             WriterHelper stdOutput,
       
   658             WriterHelper errOutput,
       
   659             Expect whatToExpt)
       
   660             throws IOException, InterruptedException, CommandExecutionException {
       
   661         ProcessBuilder pb = new ProcessBuilder(command);
       
   662 
       
   663         if (stdOutput != null) stdOutput.pipeOutput(pb);
       
   664         if (errOutput != null) errOutput.pipeOutput(pb);
       
   665 
       
   666         if (extraEnv != null) {
       
   667             pb.environment().putAll(extraEnv);
       
   668         }
       
   669 
       
   670         Process p = pb.start();
       
   671 
       
   672         if (stdOutput != null) stdOutput.readFromStream(p);
       
   673         if (errOutput != null) errOutput.readFromStream(p);
       
   674 
       
   675         int result = p.waitFor();
       
   676         if ( (result == 0 && whatToExpt == Expect.SUCCESS) ||
       
   677              (result != 0 && whatToExpt == Expect.FAIL) ) {
       
   678             return result;
       
   679         }
       
   680 
       
   681         throw new CommandExecutionException(command, whatToExpt);
       
   682     }
       
   683 
       
   684     /**
       
   685      * This set of methods can be used instead of diff when the only needed
       
   686      * result is the equality or inequality of the two given resources.
       
   687      *
       
   688      * A resource can be a file or a String list.
       
   689      */
       
   690     public static void compareLines(Path aPath, Path otherPath, String encoding)
       
   691             throws FileNotFoundException, IOException, ResourcesNotEqualException {
       
   692         compareLines(aPath, otherPath, encoding, false);
       
   693     }
       
   694 
       
   695     public static void compareLines(
       
   696             Path aPath, Path otherPath, String encoding, boolean trim)
       
   697             throws FileNotFoundException, IOException, ResourcesNotEqualException {
       
   698         Charset charset = encoding != null ?
       
   699                 Charset.forName(encoding) :
       
   700                 defaultCharset;
       
   701         List<String> list1 = Files.readAllLines(aPath, charset);
       
   702         List<String> list2 = Files.readAllLines(otherPath, charset);
       
   703         compareLines(list1, list2, trim);
       
   704     }
       
   705 
       
   706     public static void compareLines(Path path, List<String> strings, String encoding)
       
   707             throws FileNotFoundException, IOException, ResourcesNotEqualException {
       
   708         compareLines(path, strings, encoding, false);
       
   709     }
       
   710 
       
   711     public static void compareLines(Path path, List<String> strings,
       
   712             String encoding, boolean trim)
       
   713             throws FileNotFoundException, IOException, ResourcesNotEqualException {
       
   714         Charset charset = encoding != null ?
       
   715                 Charset.forName(encoding) :
       
   716                 defaultCharset;
       
   717         List<String> list = Files.readAllLines(path, charset);
       
   718         compareLines(list, strings, trim);
       
   719     }
       
   720 
       
   721     public static void compareLines(List<String> list1, List<String> list2)
       
   722             throws ResourcesNotEqualException {
       
   723         compareLines(list1, list2, false);
       
   724     }
       
   725 
       
   726     public static void compareLines(List<String> list1,
       
   727             List<String> list2, boolean trim) throws ResourcesNotEqualException {
       
   728         if ((list1 == list2) || (list1 == null && list2 == null)) return;
       
   729         if (list1.size() != list2.size())
       
   730             throw new ResourcesNotEqualException(list1, list2);
       
   731         int i = 0;
       
   732         int j = 0;
       
   733         while (i < list1.size() &&
       
   734                j < list2.size() &&
       
   735                equals(list1.get(i), list2.get(j), trim)) {
       
   736             i++; j++;
       
   737         }
       
   738         if (!(i == list1.size() && j == list2.size()))
       
   739             throw new ResourcesNotEqualException(list1, list2);
       
   740     }
       
   741 
       
   742     private static boolean equals(String s1, String s2, boolean trim) {
       
   743         return (trim ? s1.trim().equals(s2.trim()) : s1.equals(s2));
       
   744     }
       
   745 
       
   746     /**
       
   747      * A set of simple grep-like methods, looks for regExpr in text.
       
   748      * The content of text is split using the new line character as a pattern
       
   749      * and later the regExpr is seek in every split line. If a match is found,
       
   750      * the whole line is added to the result.
       
   751      */
       
   752     public static List<String> grep(String regExpr, String text, String sep) {
       
   753         return grep(regExpr, splitLines(text, sep));
       
   754     }
       
   755 
       
   756     public static List<String> grep(String regExpr, List<String> text) {
       
   757         List<String> result = new ArrayList<>();
       
   758         Pattern pattern = Pattern.compile(regExpr);
       
   759         for (String s : text) {
       
   760             if (pattern.matcher(s).find()) {
       
   761                 result.add(s);
       
   762             }
       
   763         }
       
   764         return result;
       
   765     }
       
   766 
       
   767     public static List<String> grep(String regExpr, File f)
       
   768             throws IOException {
       
   769         List<String> lines = Files.readAllLines(f.toPath(), defaultCharset);
       
   770         return grep(regExpr, lines);
       
   771     }
       
   772 
       
   773     /**
       
   774      * A touch-like method.
       
   775      */
       
   776     public static boolean touch(String fileName) {
       
   777         File file = new File(fileName);
       
   778         return touch(file);
       
   779     }
       
   780 
       
   781     public static boolean touch(File file) {
       
   782         if (file.exists()) {
       
   783             file.setLastModified(System.currentTimeMillis());
       
   784             return true;
       
   785         }
       
   786         return false;
       
   787     }
       
   788 
       
   789     public static void createJavaFile(File outFile) throws IOException {
       
   790         createJavaFile(outFile, null);
       
   791     }
       
   792 
       
   793     /**
       
   794      * A method for creating a valid but very simple java file.
       
   795      */
       
   796     public static void createJavaFile(File outFile, File superClass)
       
   797             throws IOException {
       
   798         String srcStr = "public class " + getSimpleName(outFile) + " ";
       
   799         if (superClass != null) {
       
   800             srcStr = srcStr.concat("extends " + getSimpleName(superClass) + " ");
       
   801         }
       
   802         srcStr = srcStr.concat("{}");
       
   803         try (PrintWriter ps = new PrintWriter(new FileWriter(outFile))) {
       
   804             ps.println(srcStr);
       
   805         }
       
   806     }
       
   807 
       
   808     /**
       
   809      * Creates a java file name given its source.
       
   810      * The file is created in the working directory, creating a directory
       
   811      * tree if there is a package declaration.
       
   812      */
       
   813     public static void createJavaFileFromSource(String source) throws IOException {
       
   814         createJavaFileFromSource(null, source);
       
   815     }
       
   816 
       
   817     /**
       
   818      * Creates a java file name given its source.
       
   819      * The file is created in the working directory, creating a directory
       
   820      * tree if there is a package declaration or the argument initialPath
       
   821      * has a valid path.
       
   822      *
       
   823      * e.i. if initialPath is foo/ and the source is:
       
   824      * package bar;
       
   825      *
       
   826      * public class bazz {}
       
   827      *
       
   828      * this method will create the file foo/bar/bazz.java in the working
       
   829      * directory.
       
   830      */
       
   831     public static void createJavaFileFromSource(Path initialPath,
       
   832             String source) throws IOException {
       
   833         String fileName = getJavaFileNameFromSource(source);
       
   834         String dirTree = getDirTreeFromSource(source);
       
   835         Path path = (dirTree != null) ?
       
   836                 Paths.get(dirTree, fileName) :
       
   837                 Paths.get(fileName);
       
   838         path = (initialPath != null) ?
       
   839                 initialPath.resolve(path):
       
   840                 path;
       
   841         writeFile(path, source);
       
   842     }
       
   843 
       
   844     static Pattern publicClassPattern =
       
   845             Pattern.compile("public\\s+(?:class|enum|interface){1}\\s+(\\w+)");
       
   846     static Pattern packageClassPattern =
       
   847             Pattern.compile("(?:class|enum|interface){1}\\s+(\\w+)");
       
   848 
       
   849     /**
       
   850      * Extracts the java file name from the class declaration.
       
   851      * This method is intended for simple files and uses regular expressions,
       
   852      * so comments matching the pattern can make the method fail.
       
   853      */
       
   854     static String getJavaFileNameFromSource(String source) {
       
   855         String className = null;
       
   856         Matcher matcher = publicClassPattern.matcher(source);
       
   857         if (matcher.find()) {
       
   858             className = matcher.group(1) + ".java";
       
   859         } else {
       
   860             matcher = packageClassPattern.matcher(source);
       
   861             if (matcher.find()) {
       
   862                 className = matcher.group(1) + ".java";
       
   863             } else {
       
   864                 throw new AssertionError("Could not extract the java class " +
       
   865                         "name from the provided source");
       
   866             }
       
   867         }
       
   868         return className;
       
   869     }
       
   870 
       
   871     static Pattern packagePattern =
       
   872             Pattern.compile("package\\s+(((?:\\w+\\.)*)(?:\\w+))");
       
   873 
       
   874     /**
       
   875      * Extracts the path from the package declaration if present.
       
   876      * This method is intended for simple files and uses regular expressions,
       
   877      * so comments matching the pattern can make the method fail.
       
   878      */
       
   879     private static String getDirTreeFromSource(String source) {
       
   880         Matcher matcher = packagePattern.matcher(source);
       
   881         return matcher.find() ?
       
   882             matcher.group(1).replace(".", File.separator) :
       
   883             null;
       
   884     }
       
   885 
       
   886     /**
       
   887      * A method for creating a jar's manifest file with supplied data.
       
   888      */
       
   889     public static void mkManifestWithClassPath(String mainClass,
       
   890             String... classes) throws IOException {
       
   891         List <String> lines = new ArrayList<>();
       
   892 
       
   893         StringBuilder sb = new StringBuilder("Class-Path: ".length() +
       
   894                 classes[0].length()).append("Class-Path: ").append(classes[0]);
       
   895         for (int i = 1; i < classes.length; i++) {
       
   896             sb.append(" ").append(classes[i]);
       
   897         }
       
   898         lines.add(sb.toString());
       
   899         if (mainClass != null) {
       
   900             lines.add(new StringBuilder("Main-Class: ".length() +
       
   901                       mainClass.length())
       
   902                       .append("Main-Class: ")
       
   903                       .append(mainClass).toString());
       
   904         }
       
   905         Files.write(Paths.get("MANIFEST.MF"), lines,
       
   906                     StandardOpenOption.CREATE, StandardOpenOption.WRITE,
       
   907                     StandardOpenOption.TRUNCATE_EXISTING);
       
   908     }
       
   909 
       
   910     /**
       
   911      * A utility method to obtain the file name.
       
   912      */
       
   913     static String getSimpleName(File inFile) {
       
   914         return inFile.toPath().getFileName().toString();
       
   915     }
       
   916 
       
   917     /**
       
   918      * A method to write to a file, the directory tree is created if needed.
       
   919      */
       
   920     public static File writeFile(Path path, String body) throws IOException {
       
   921         File result;
       
   922         if (path.getParent() != null) {
       
   923             Files.createDirectories(path.getParent());
       
   924         }
       
   925         try (FileWriter out = new FileWriter(result = path.toAbsolutePath().toFile())) {
       
   926             out.write(body);
       
   927         }
       
   928         return result;
       
   929     }
       
   930 
       
   931     public static File writeFile(String path, String body) throws IOException {
       
   932         return writeFile(Paths.get(path), body);
       
   933     }
       
   934 
       
   935     /**
       
   936      * A rm-like method, the file is deleted only if it exists.
       
   937      */
       
   938     public static void rm(Path path) throws Exception {
       
   939         Files.deleteIfExists(path);
       
   940     }
       
   941 
       
   942     public static void rm(String filename) throws Exception {
       
   943         rm(Paths.get(filename));
       
   944     }
       
   945 
       
   946     public static void rm(File f) throws Exception {
       
   947         rm(f.toPath());
       
   948     }
       
   949 
       
   950     /**
       
   951      * Copy source file to destination file.
       
   952      */
       
   953     public static void copyFile(File destfile, File srcfile)
       
   954         throws IOException {
       
   955         copyFile(destfile.toPath(), srcfile.toPath());
       
   956     }
       
   957 
       
   958     public static void copyFile(Path destPath, Path srcPath)
       
   959         throws IOException {
       
   960         Files.createDirectories(destPath);
       
   961         Files.copy(srcPath, destPath, REPLACE_EXISTING);
       
   962     }
       
   963 
       
   964     /**
       
   965      * Splits a String using the System's line separator character as splitting point.
       
   966      */
       
   967     public static List<String> splitLines(String lines, String sep) {
       
   968         return Arrays.asList(lines.split(sep));
       
   969     }
       
   970 
       
   971     /**
       
   972      * Converts a String list into one String by appending the System's line separator
       
   973      * character after each component.
       
   974      */
       
   975     private static String listToString(List<String> lines) {
       
   976         StringBuilder sb = new StringBuilder();
       
   977         for (String s : lines) {
       
   978             sb.append(s).append(lineSeparator);
       
   979         }
       
   980         return sb.toString();
       
   981     }
       
   982 
       
   983     /**
       
   984      * Returns true if the OS is a Windows version.
       
   985      */
       
   986     public static boolean isWindows() {
       
   987         String osName = System.getProperty("os.name");
       
   988         return osName.toUpperCase().startsWith("WINDOWS");
       
   989     }
       
   990 
       
   991     /**
       
   992      * Class representing an in-memory java source file. It is able to extract
       
   993      * the file name from simple source codes using regular expressions.
       
   994      */
       
   995     public static class JavaSource extends SimpleJavaFileObject {
       
   996         String source;
       
   997         String name;
       
   998 
       
   999         public JavaSource(String className, String source) {
       
  1000             super(URI.create(className),
       
  1001                     JavaFileObject.Kind.SOURCE);
       
  1002             this.name = className;
       
  1003             this.source = source;
       
  1004         }
       
  1005 
       
  1006         public JavaSource(String source) {
       
  1007             super(URI.create(getJavaFileNameFromSource(source)),
       
  1008                     JavaFileObject.Kind.SOURCE);
       
  1009             this.name = getJavaFileNameFromSource(source);
       
  1010             this.source = source;
       
  1011         }
       
  1012 
       
  1013         @Override
       
  1014         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
       
  1015             return source;
       
  1016         }
       
  1017     }
       
  1018 
       
  1019     /**
       
  1020      * A file manager for compiling strings to byte arrays.
       
  1021      * This file manager delegates to another file manager
       
  1022      * to lookup classes on boot class path.
       
  1023      */
       
  1024     public static final class MemoryFileManager extends ForwardingJavaFileManager {
       
  1025         /**
       
  1026          * Maps binary class names to class files stored as byte arrays.
       
  1027          */
       
  1028         private final Map<String, byte[]> classes;
       
  1029 
       
  1030         /**
       
  1031          * Construct a memory file manager which delegates to the specified
       
  1032          * file manager for unknown sources.
       
  1033          * @param fileManager a file manager used to look up class files on class path, etc.
       
  1034          */
       
  1035         public MemoryFileManager(JavaFileManager fileManager) {
       
  1036             super(fileManager);
       
  1037             classes = new HashMap<>();
       
  1038         }
       
  1039 
       
  1040         @java.lang.Override
       
  1041         public JavaFileObject getJavaFileForOutput(Location location,
       
  1042                                                    String name,
       
  1043                                                    Kind kind,
       
  1044                                                    FileObject sibling)
       
  1045             throws UnsupportedOperationException
       
  1046         {
       
  1047             return new JavaClassInArray(name);
       
  1048         }
       
  1049 
       
  1050         /**
       
  1051          * A file object representing a Java class file stored in a byte array.
       
  1052          */
       
  1053         private class JavaClassInArray extends SimpleJavaFileObject {
       
  1054 
       
  1055             private final String name;
       
  1056 
       
  1057             /**
       
  1058              * Constructs a JavaClassInArray object.
       
  1059              * @param name binary name of the class to be stored in this file object
       
  1060              */
       
  1061             JavaClassInArray(String name) {
       
  1062                 super(URI.create("mfm:///" + name.replace('.','/') + Kind.CLASS.extension),
       
  1063                       Kind.CLASS);
       
  1064                 this.name = name;
       
  1065             }
       
  1066 
       
  1067             public OutputStream openOutputStream() {
       
  1068                 return new FilterOutputStream(new ByteArrayOutputStream()) {
       
  1069                     public void close() throws IOException {
       
  1070                         out.close();
       
  1071                         ByteArrayOutputStream bos = (ByteArrayOutputStream)out;
       
  1072                         classes.put(name, bos.toByteArray());
       
  1073                     }
       
  1074                 };
       
  1075             }
       
  1076         }
       
  1077 
       
  1078     }
       
  1079 
       
  1080 }