| /* |
| * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @test |
| * @bug 8065360 |
| * @summary The test checks dependencies through type parameters and implements/extends statements. |
| * @library /tools/lib |
| * @modules jdk.compiler/com.sun.tools.javac.api |
| * jdk.compiler/com.sun.tools.javac.file |
| * jdk.compiler/com.sun.tools.javac.main |
| * jdk.jdeps/com.sun.tools.javap |
| * @build ToolBox ImportDependenciesTest |
| * @run main ImportDependenciesTest |
| */ |
| |
| import javax.tools.JavaCompiler; |
| import javax.tools.ToolProvider; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| /** |
| * The test checks that code which contains dependencies through type parameters, |
| * implements/extends statements compiles properly. All combinations of |
| * import types are tested. In addition, the test checks various combinations |
| * of classes. |
| */ |
| public class ImportDependenciesTest { |
| |
| private static final String sourceTemplate = |
| "package pkg;\n" + |
| "#IMPORT\n" + |
| "public class Test {\n" + |
| " static #CLASS_TYPE InnerClass#TYPE_PARAMETER #PARENT {\n" + |
| " static class Inner1 {\n" + |
| " }\n" + |
| " interface Inner2 {\n" + |
| " }\n" + |
| " interface Inner3 {\n" + |
| " }\n" + |
| " }\n" + |
| " static class InnerClass1 {\n" + |
| " static class IInner1 {\n" + |
| " }\n" + |
| " }\n" + |
| " static class InnerInterface1 {\n" + |
| " interface IInner2 {\n" + |
| " }\n" + |
| " }\n" + |
| " static class InnerInterface2 {\n" + |
| " interface IInner3 {\n" + |
| " }\n" + |
| " }\n" + |
| "}"; |
| |
| public static void main(String[] args) { |
| new ImportDependenciesTest().test(); |
| } |
| |
| public void test() { |
| List<List<InnerClass>> typeParameters = InnerClass.getAllCombinationsForTypeParameter(); |
| List<List<InnerClass>> parents = InnerClass.getAllCombinationsForInheritance(); |
| int passed = 0; |
| int total = 0; |
| for (ClassType classType : ClassType.values()) { |
| for (List<InnerClass> parent : parents) { |
| if (!classType.canBeInherited(parent)) { |
| continue; |
| } |
| for (List<InnerClass> typeParameter : typeParameters) { |
| List<InnerClass> innerClasses = new ArrayList<>(typeParameter); |
| innerClasses.addAll(parent); |
| for (ImportType importType : ImportType.values()) { |
| ++total; |
| String source = sourceTemplate |
| .replace("#IMPORT", importType.generateImports(innerClasses)) |
| .replace("#CLASS_TYPE", classType.getClassType()) |
| .replace("#TYPE_PARAMETER", generateTypeParameter(typeParameter)) |
| .replace("#PARENT", classType.generateInheritanceString(parent)); |
| CompilationResult result = compile(new ToolBox.JavaSource("pkg/Test.java", source)); |
| if (!result.isSuccessful) { |
| echo("Compilation failed!"); |
| echo(source); |
| echo(result.message); |
| echo(); |
| } else { |
| ++passed; |
| } |
| } |
| } |
| } |
| } |
| String message = String.format( |
| "Total test cases run: %d, passed: %d, failed: %d.", |
| total, passed, total - passed); |
| if (passed != total) { |
| throw new RuntimeException(message); |
| } |
| echo(message); |
| } |
| |
| private String generateTypeParameter(List<InnerClass> typeParameters) { |
| if (typeParameters.isEmpty()) { |
| return ""; |
| } |
| return String.format("<T extends %s>", typeParameters.stream() |
| .map(InnerClass::getSimpleName) |
| .collect(Collectors.joining(" & "))); |
| } |
| |
| private static class CompilationResult { |
| public final boolean isSuccessful; |
| public final String message; |
| |
| public CompilationResult(boolean isSuccessful, String message) { |
| this.isSuccessful = isSuccessful; |
| this.message = message; |
| } |
| } |
| |
| private CompilationResult compile(ToolBox.JavaSource...sources) { |
| StringWriter writer = new StringWriter(); |
| JavaCompiler jc = ToolProvider.getSystemJavaCompiler(); |
| Boolean call = jc.getTask(writer, null, null, null, null, Arrays.asList(sources)).call(); |
| return new CompilationResult(call, writer.toString().replace(ToolBox.lineSeparator, "\n")); |
| } |
| |
| public void echo() { |
| echo(""); |
| } |
| |
| public void echo(String output) { |
| printf(output + "\n"); |
| } |
| |
| public void printf(String template, Object...args) { |
| System.err.print(String.format(template, args).replace("\n", ToolBox.lineSeparator)); |
| } |
| |
| enum ImportType { |
| IMPORT("import"), STATIC_IMPORT("import static"), |
| IMPORT_ON_DEMAND("import"), STATIC_IMPORT_ON_DEMAND("import static"); |
| |
| private final String importType; |
| private ImportType(String importType) { |
| this.importType = importType; |
| } |
| |
| private boolean isOnDemand() { |
| return this == IMPORT_ON_DEMAND || this == STATIC_IMPORT_ON_DEMAND; |
| } |
| |
| public String generateImports(List<InnerClass> innerClasses) { |
| return innerClasses.stream() |
| .map(i -> isOnDemand() ? i.getPackageName() + ".*" : i.getCanonicalName()) |
| .distinct() |
| .map(s -> String.format("%s %s;", importType, s)) |
| .collect(Collectors.joining("\n")); |
| } |
| } |
| |
| enum ClassType { |
| CLASS("class") { |
| @Override |
| public boolean canBeInherited(List<InnerClass> innerClasses) { |
| return true; |
| } |
| |
| @Override |
| public String generateInheritanceString(List<InnerClass> innerClasses) { |
| if (innerClasses.isEmpty()) { |
| return ""; |
| } |
| StringBuilder sb = new StringBuilder(); |
| InnerClass firstClass = innerClasses.get(0); |
| if (firstClass.isClass()) { |
| sb.append("extends ").append(firstClass.getSimpleName()).append(" "); |
| } |
| String str = innerClasses.stream() |
| .filter(x -> !x.isClass()) |
| .map(InnerClass::getSimpleName) |
| .collect(Collectors.joining(", ")); |
| if (!str.isEmpty()) { |
| sb.append("implements ").append(str); |
| } |
| return sb.toString(); |
| } |
| }, INTERFACE("interface") { |
| @Override |
| public boolean canBeInherited(List<InnerClass> innerClasses) { |
| return !innerClasses.stream().anyMatch(InnerClass::isClass); |
| } |
| |
| @Override |
| public String generateInheritanceString(List<InnerClass> innerClasses) { |
| if (innerClasses.isEmpty()) { |
| return ""; |
| } |
| return "extends " + innerClasses.stream() |
| .map(InnerClass::getSimpleName) |
| .collect(Collectors.joining(", ")); |
| } |
| }; |
| |
| private final String classType; |
| private ClassType(String classType) { |
| this.classType = classType; |
| } |
| |
| public String getClassType() { |
| return classType; |
| } |
| |
| public abstract boolean canBeInherited(List<InnerClass> innerClasses); |
| |
| public abstract String generateInheritanceString(List<InnerClass> innerClasses); |
| } |
| |
| enum InnerClass { |
| INNER_1("pkg.Test.InnerClass.Inner1", true), |
| INNER_2("pkg.Test.InnerClass.Inner2", true), |
| INNER_3("pkg.Test.InnerClass.Inner3", true), |
| IINNER_1("pkg.Test.InnerClass1.IInner1", false), |
| IINNER_2("pkg.Test.InnerInterface1.IInner2", false), |
| IINNER_3("pkg.Test.InnerInterface2.IInner3", false); |
| |
| private final String canonicalName; |
| private final boolean isForTypeParameter; |
| |
| private InnerClass(String canonicalName, boolean isForTypeParameter) { |
| this.canonicalName = canonicalName; |
| this.isForTypeParameter = isForTypeParameter; |
| } |
| |
| private static List<List<InnerClass>> getAllCombinations(boolean isTypeParameter) { |
| List<List<InnerClass>> result = new ArrayList<>(); |
| List<InnerClass> tmpl = Stream.of(InnerClass.values()) |
| .filter(i -> i.isForTypeParameter() == isTypeParameter) |
| .collect(Collectors.toCollection(ArrayList::new)); |
| result.add(Arrays.asList()); |
| for (int i = 0; i < tmpl.size(); ++i) { |
| result.add(Arrays.asList(tmpl.get(i))); |
| for (int j = i + 1; j < tmpl.size(); ++j) { |
| result.add(Arrays.asList(tmpl.get(i), tmpl.get(j))); |
| } |
| } |
| result.add(tmpl); |
| return result; |
| } |
| |
| public static List<List<InnerClass>> getAllCombinationsForTypeParameter() { |
| return getAllCombinations(true); |
| } |
| |
| public static List<List<InnerClass>> getAllCombinationsForInheritance() { |
| return getAllCombinations(false); |
| } |
| |
| public String getCanonicalName() { |
| return canonicalName; |
| } |
| |
| public String getSimpleName() { |
| String cName = getCanonicalName(); |
| return cName.substring(cName.lastIndexOf('.') + 1); |
| } |
| |
| public String getPackageName() { |
| String cName = getCanonicalName(); |
| int dotIndex = cName.lastIndexOf('.'); |
| return dotIndex == -1 ? "" : cName.substring(0, dotIndex); |
| } |
| |
| public boolean isClass() { |
| return this == INNER_1 || this == IINNER_1; |
| } |
| private boolean isForTypeParameter() { |
| return isForTypeParameter; |
| } |
| } |
| } |