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