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 
ModuleInfoMaker(Path dir)49     public ModuleInfoMaker(Path dir) {
50         this.dir = dir;
51     }
52 
53     /**
54      * Create java source files of the given module
55      */
writeJavaFiles(String module, String moduleInfoJava, String... contents)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      */
compile(String module, Path dest, String... options)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;
JavaSource(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          */
write(Path dir)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          */
getJavaFileNameFromSource(String source)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 }
130