Module.java revision 3170:dc017a37aac5
1/*
2 * Copyright (c) 2014, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.jdeps;
27
28import java.util.Collections;
29import java.util.HashMap;
30import java.util.HashSet;
31import java.util.Map;
32import java.util.Objects;
33import java.util.Set;
34
35/**
36 * JDeps internal representation of module for dependency analysis.
37 */
38final class Module extends Archive {
39    private final String moduleName;
40    private final Map<String, Boolean> requires;
41    private final Map<String, Set<String>> exports;
42    private final Set<String> packages;
43
44    private Module(ClassFileReader reader, String name,
45                   Map<String, Boolean> requires,
46                   Map<String, Set<String>> exports,
47                   Set<String> packages) {
48        super(name, reader);
49        this.moduleName = name;
50        this.requires = Collections.unmodifiableMap(requires);
51        this.exports = Collections.unmodifiableMap(exports);
52        this.packages = Collections.unmodifiableSet(packages);
53    }
54
55    public String name() {
56        return moduleName;
57    }
58
59    public Map<String, Boolean> requires() {
60        return requires;
61    }
62
63    public Map<String, Set<String>> exports() {
64        return exports;
65    }
66
67    public Set<String> packages() {
68        return packages;
69    }
70
71    /**
72     * Tests if this module can read m
73     */
74    public boolean canRead(Module m) {
75        // ## TODO: handle "re-exported=true"
76        // all JDK modules require all modules containing its direct dependences
77        // should not be an issue
78        return requires.containsKey(m.name());
79    }
80
81    /**
82     * Tests if a given fully-qualified name is an exported type.
83     */
84    public boolean isExported(String cn) {
85        int i = cn.lastIndexOf('.');
86        String pn = i > 0 ? cn.substring(0, i) : "";
87
88        return isExportedPackage(pn);
89    }
90
91    /**
92     * Tests if a given package name is exported.
93     */
94    public boolean isExportedPackage(String pn) {
95        return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false;
96    }
97
98    /**
99     * Tests if the given classname is accessible to module m
100     */
101    public boolean isAccessibleTo(String classname, Module m) {
102        int i = classname.lastIndexOf('.');
103        String pn = i > 0 ? classname.substring(0, i) : "";
104        if (!packages.contains(pn)) {
105            throw new IllegalArgumentException(classname + " is not a member of module " + name());
106        }
107
108        if (m != null && !m.canRead(this)) {
109            trace("%s not readable by %s%n", this.name(), m.name());
110            return false;
111        }
112
113        // exported API
114        Set<String> ms = exports().get(pn);
115        String mname = m != null ? m.name() : "unnamed";
116        if (ms == null) {
117            trace("%s not exported in %s%n", classname, this.name());
118        } else if (!(ms.isEmpty() || ms.contains(mname))) {
119            trace("%s not permit to %s %s%n", classname, mname, ms);
120        }
121        return ms != null && (ms.isEmpty() || ms.contains(mname));
122    }
123
124    private static final boolean traceOn = Boolean.getBoolean("jdeps.debug");
125    private void trace(String fmt, Object... args) {
126        if (traceOn) {
127            System.err.format(fmt, args);
128        }
129    }
130
131    @Override
132    public boolean equals(Object ob) {
133        if (!(ob instanceof Module))
134            return false;
135        Module that = (Module)ob;
136        return (moduleName.equals(that.moduleName)
137                && requires.equals(that.requires)
138                && exports.equals(that.exports)
139                && packages.equals(that.packages));
140    }
141
142    @Override
143    public int hashCode() {
144        int hc = moduleName.hashCode();
145        hc = hc * 43 + requires.hashCode();
146        hc = hc * 43 + exports.hashCode();
147        hc = hc * 43 + packages.hashCode();
148        return hc;
149    }
150
151    @Override
152    public String toString() {
153        return name();
154    }
155
156    public final static class Builder {
157        String name;
158        ClassFileReader reader;
159        final Map<String, Boolean> requires = new HashMap<>();
160        final Map<String, Set<String>> exports = new HashMap<>();
161        final Set<String> packages = new HashSet<>();
162
163        public Builder() {
164        }
165
166        public Builder name(String n) {
167            name = n;
168            return this;
169        }
170
171        public Builder require(String d, boolean reexport) {
172         //   System.err.format("%s depend %s reexports %s%n", name, d, reexport);
173            requires.put(d, reexport);
174            return this;
175        }
176
177        public Builder packages(Set<String> pkgs) {
178            packages.addAll(pkgs);
179            return this;
180        }
181
182        public Builder export(String p, Set<String> ms) {
183            Objects.requireNonNull(p);
184            Objects.requireNonNull(ms);
185            exports.put(p, new HashSet<>(ms));
186            return this;
187        }
188        public Builder classes(ClassFileReader.ModuleClassReader reader) {
189            this.reader = reader;
190            return this;
191        }
192
193        public Module build() {
194            Module m = new Module(reader, name, requires, exports, packages);
195            return m;
196        }
197    }
198}
199