1 /*
   2  * Copyright (c) 2016, 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.BufferedWriter;
  25 import java.io.IOException;
  26 import java.nio.file.Files;
  27 import java.nio.file.Path;
  28 import java.util.Arrays;
  29 import java.util.regex.Matcher;
  30 import java.util.regex.Pattern;
  31 import java.util.stream.Stream;
  32 
  33 import static org.testng.Assert.assertTrue;
  34 
  35 public class ExplodedModuleBuilder {
  36     private static String MODULE_INFO_JAVA = "module-info.java";
  37     private static Pattern MODULE_PATTERN =
  38         Pattern.compile("module\\s+((?:\\w+\\.)*)");
  39     private static Pattern PACKAGE_PATTERN =
  40                        Pattern.compile("package\\s+(((?:\\w+\\.)*)(?:\\w+))");
  41     private static Pattern CLASS_PATTERN =
  42           Pattern.compile("(?:public\\s+)?(?:class|enum|interface)\\s+(\\w+)");
  43 
  44     final Path dir;
  45     ExplodedModuleBuilder(Path dir) {
  46         this.dir = dir;
  47     }
  48 
  49     void writeJavaFiles(String module, String moduleInfoJava, String... contents)
  50         throws IOException
  51     {
  52         Path msrc = dir.resolve(module);
  53         new JavaSource(moduleInfoJava).write(msrc);
  54         for (String c : contents) {
  55             new JavaSource(c).write(msrc);
  56         }
  57     }
  58 
  59     void compile(String module, Path dest, String... options) throws IOException {
  60         Path msrc = dir.resolve(module);
  61         Stream<String> args =
  62             Stream.concat(Arrays.stream(options),
  63                           Stream.of("--module-source-path",
  64                                     dir.toString()));
  65         assertTrue(CompilerUtils.compile(msrc, dest, args.toArray(String[]::new)),
  66                    "Fail to compile " + module);
  67     }
  68 
  69     static class JavaSource {
  70         final String source;
  71         JavaSource(String source) {
  72             this.source = source;
  73         }
  74 
  75         /**
  76          * Writes the source code to a file in a specified directory.
  77          * @param dir the directory
  78          * @throws IOException if there is a problem writing the file
  79          */
  80         public void write(Path dir) throws IOException {
  81             Path file = dir.resolve(getJavaFileNameFromSource(source));
  82             Files.createDirectories(file.getParent());
  83             try (BufferedWriter out = Files.newBufferedWriter(file)) {
  84                 out.write(source.replace("\n", System.lineSeparator()));
  85             }
  86         }
  87 
  88         /**
  89          * Extracts the Java file name from the class declaration.
  90          * This method is intended for simple files and uses regular expressions,
  91          * so comments matching the pattern can make the method fail.
  92          */
  93         static String getJavaFileNameFromSource(String source) {
  94             String packageName = null;
  95 
  96             Matcher matcher = MODULE_PATTERN.matcher(source);
  97             if (matcher.find())
  98                 return MODULE_INFO_JAVA;
  99 
 100             matcher = PACKAGE_PATTERN.matcher(source);
 101             if (matcher.find())
 102                 packageName = matcher.group(1).replace(".", "/");
 103 
 104             matcher = CLASS_PATTERN.matcher(source);
 105             if (matcher.find()) {
 106                 String className = matcher.group(1) + ".java";
 107                 return (packageName == null) ? className : packageName + "/" + className;
 108             } else if (packageName != null) {
 109                 return packageName + "/package-info.java";
 110             } else {
 111                 throw new Error("Could not extract the java class " +
 112                     "name from the provided source");
 113             }
 114         }
 115     }
 116 }