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
25import com.sun.tools.jdeps.DepsAnalyzer;
26
27import java.util.Collections;
28import java.util.LinkedHashMap;
29import java.util.Map;
30import java.util.Set;
31
32import static com.sun.tools.jdeps.DepsAnalyzer.Info.*;
33import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
34import static java.lang.module.ModuleDescriptor.*;
35
36import static org.testng.Assert.assertEquals;
37import static org.testng.Assert.assertTrue;
38
39
40public 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
54    ModuleMetaData(String name) {
55        this(name, true);
56    }
57
58    ModuleMetaData(String name, boolean isNamed) {
59        this.moduleName = name;
60        this.isNamed = isNamed;
61        requires(JAVA_BASE);  // implicit requires
62    }
63
64    String name() {
65        return moduleName;
66    }
67
68    ModuleMetaData requires(String name) {
69        requires.put(name, new ModuleRequires(name));
70        return this;
71    }
72
73    ModuleMetaData requiresTransitive(String name) {
74        requires.put(name, new ModuleRequires(name, TRANSITIVE));
75        return this;
76    }
77
78    // for unnamed module
79    ModuleMetaData depends(String name) {
80        requires.put(name, new ModuleRequires(name));
81        return this;
82    }
83
84    ModuleMetaData reference(String origin, String target, String module) {
85        return dependence(origin, target, module, "");
86    }
87
88    ModuleMetaData internal(String origin, String target, String module) {
89        return dependence(origin, target, module, INTERNAL);
90    }
91
92    ModuleMetaData qualified(String origin, String target, String module) {
93        return dependence(origin, target, module, QUALIFIED);
94    }
95
96    ModuleMetaData jdkInternal(String origin, String target, String module) {
97        return dependence(origin, target, module, JDK_INTERNAL);
98    }
99    ModuleMetaData removedJdkInternal(String origin, String target) {
100        return dependence(origin, target, REMOVED_JDK_INTERNAL, REMOVED_JDK_INTERNAL);
101    }
102
103    ModuleMetaData exports(String pn, Set<String> targets) {
104        exports.put(pn, targets);
105        return this;
106    }
107
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
113    String key(String origin, String target) {
114        return origin + ":" + target;
115    }
116
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
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
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
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
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
177        ModuleRequires(String name) {
178            this.name = name;
179            this.mod = null;
180        }
181
182        ModuleRequires(String name, Requires.Modifier mod) {
183            this.name = name;
184            this.mod = mod;
185        }
186
187        Set<Requires.Modifier> modifiers() {
188            return mod != null ? Set.of(mod) : Collections.emptySet();
189        }
190
191        @Override
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
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
211        public String toString() {
212            return String.format("%s -> %s (%s) %s", origin, target, module, access);
213        }
214    }
215}
216