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 
25 import com.sun.tools.jdeps.DepsAnalyzer;
26 
27 import java.util.Collections;
28 import java.util.LinkedHashMap;
29 import java.util.Map;
30 import java.util.Set;
31 
32 import static com.sun.tools.jdeps.DepsAnalyzer.Info.*;
33 import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
34 import static java.lang.module.ModuleDescriptor.*;
35 
36 import static org.testng.Assert.assertEquals;
37 import static org.testng.Assert.assertTrue;
38 
39 
40 public class ModuleMetaData {
41     public static final String JAVA_BASE = "java.base";
42 
43     static final String INTERNAL = "(internal)";
44     static final String QUALIFIED = "(qualified)";
45     static final String JDK_INTERNAL = "JDK internal API";
46     static final String REMOVED_JDK_INTERNAL = "JDK removed internal API";
47 
48     final String moduleName;
49     final boolean isNamed;
50     final Map<String, ModuleRequires> requires = new LinkedHashMap<>();
51     final Map<String, Dependence> references = new LinkedHashMap<>();
52     final Map<String, Set<String>> exports = new LinkedHashMap<>();
53 
ModuleMetaData(String name)54     ModuleMetaData(String name) {
55         this(name, true);
56     }
57 
ModuleMetaData(String name, boolean isNamed)58     ModuleMetaData(String name, boolean isNamed) {
59         this.moduleName = name;
60         this.isNamed = isNamed;
61         requires(JAVA_BASE);  // implicit requires
62     }
63 
name()64     String name() {
65         return moduleName;
66     }
67 
requires(String name)68     ModuleMetaData requires(String name) {
69         requires.put(name, new ModuleRequires(name));
70         return this;
71     }
72 
requiresTransitive(String name)73     ModuleMetaData requiresTransitive(String name) {
74         requires.put(name, new ModuleRequires(name, TRANSITIVE));
75         return this;
76     }
77 
78     // for unnamed module
depends(String name)79     ModuleMetaData depends(String name) {
80         requires.put(name, new ModuleRequires(name));
81         return this;
82     }
83 
reference(String origin, String target, String module)84     ModuleMetaData reference(String origin, String target, String module) {
85         return dependence(origin, target, module, "");
86     }
87 
internal(String origin, String target, String module)88     ModuleMetaData internal(String origin, String target, String module) {
89         return dependence(origin, target, module, INTERNAL);
90     }
91 
qualified(String origin, String target, String module)92     ModuleMetaData qualified(String origin, String target, String module) {
93         return dependence(origin, target, module, QUALIFIED);
94     }
95 
jdkInternal(String origin, String target, String module)96     ModuleMetaData jdkInternal(String origin, String target, String module) {
97         return dependence(origin, target, module, JDK_INTERNAL);
98     }
removedJdkInternal(String origin, String target)99     ModuleMetaData removedJdkInternal(String origin, String target) {
100         return dependence(origin, target, REMOVED_JDK_INTERNAL, REMOVED_JDK_INTERNAL);
101     }
102 
exports(String pn, Set<String> targets)103     ModuleMetaData exports(String pn, Set<String> targets) {
104         exports.put(pn, targets);
105         return this;
106     }
107 
dependence(String origin, String target, String module, String access)108     private ModuleMetaData dependence(String origin, String target, String module, String access) {
109         references.put(key(origin, target), new Dependence(origin, target, module, access));
110         return this;
111     }
112 
key(String origin, String target)113     String key(String origin, String target) {
114         return origin + ":" + target;
115     }
116 
checkRequires(String name, Set<DepsAnalyzer.Node> adjacentNodes)117     void checkRequires(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
118         // System.err.format("%s: Expected %s Found %s %n", name, requires, adjacentNodes);
119         adjacentNodes.stream()
120             .forEach(v -> checkRequires(v.name));
121         assertEquals(adjacentNodes.size(), requires.size());
122     }
123 
checkRequires(String name)124     void checkRequires(String name) {
125         ModuleRequires req = requires.get(name);
126         if (req == null)
127             System.err.println(moduleName + ": unexpected requires " + name);
128         assertTrue(requires.containsKey(name));
129     }
130 
checkRequires(Requires require)131     void checkRequires(Requires require) {
132         String name = require.name();
133         if (name.equals(JAVA_BASE))
134             return;
135 
136         ModuleRequires req = requires.get(name);
137         if (req == null)
138             System.err.format("%s: unexpected dependence %s%n", moduleName, name);
139 
140         assertTrue(requires.containsKey(name));
141 
142         assertEquals(require.modifiers(), req.modifiers());
143     }
144 
checkDependences(String name, Set<DepsAnalyzer.Node> adjacentNodes)145     void checkDependences(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
146         // System.err.format("%s: Expected %s Found %s %n", name, references, adjacentNodes);
147 
148         adjacentNodes.stream()
149             .forEach(v -> checkDependence(name, v.name, v.source, v.info));
150         assertEquals(adjacentNodes.size(), references.size());
151     }
152 
checkDependence(String origin, String target, String module, DepsAnalyzer.Info info)153     void checkDependence(String origin, String target, String module, DepsAnalyzer.Info info) {
154         String key = key(origin, target);
155         Dependence dep = references.get(key);
156         String access = "";
157         if (info == QUALIFIED_EXPORTED_API)
158             access = QUALIFIED;
159         else if (info == JDK_INTERNAL_API)
160             access = JDK_INTERNAL;
161         else if (info == JDK_REMOVED_INTERNAL_API)
162             access = REMOVED_JDK_INTERNAL;
163         else if (info == INTERNAL_API)
164             access = INTERNAL;
165 
166         assertTrue(references.containsKey(key));
167 
168         assertEquals(dep.access, access);
169         assertEquals(dep.module, module);
170     }
171 
172 
173     public static class ModuleRequires {
174         final String name;
175         final Requires.Modifier mod;
176 
ModuleRequires(String name)177         ModuleRequires(String name) {
178             this.name = name;
179             this.mod = null;
180         }
181 
ModuleRequires(String name, Requires.Modifier mod)182         ModuleRequires(String name, Requires.Modifier mod) {
183             this.name = name;
184             this.mod = mod;
185         }
186 
modifiers()187         Set<Requires.Modifier> modifiers() {
188             return mod != null ? Set.of(mod) : Collections.emptySet();
189         }
190 
191         @Override
toString()192         public String toString() {
193             return name;
194         }
195     }
196 
197     public static class Dependence {
198         final String origin;
199         final String target;
200         final String module;
201         final String access;
202 
Dependence(String origin, String target, String module, String access)203         Dependence(String origin, String target, String module, String access) {
204             this.origin = origin;
205             this.target = target;
206             this.module = module;
207             this.access = access;
208         }
209 
210         @Override
toString()211         public String toString() {
212             return String.format("%s -> %s (%s) %s", origin, target, module, access);
213         }
214     }
215 }
216