Module.java revision 2942:08092deced3f
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 */
25package com.sun.tools.jdeps;
26
27import java.util.Collections;
28import java.util.HashMap;
29import java.util.HashSet;
30import java.util.Map;
31import java.util.Objects;
32import java.util.Set;
33
34/**
35 * JDeps internal representation of module for dependency analysis.
36 */
37final class Module extends Archive {
38    private final String moduleName;
39    private final Map<String, Boolean> requires;
40    private final Map<String, Set<String>> exports;
41    private final Set<String> packages;
42
43    private Module(ClassFileReader reader, String name,
44                   Map<String, Boolean> requires,
45                   Map<String, Set<String>> exports,
46                   Set<String> packages) {
47        super(name, reader);
48        this.moduleName = name;
49        this.requires = Collections.unmodifiableMap(requires);
50        this.exports = Collections.unmodifiableMap(exports);
51        this.packages = Collections.unmodifiableSet(packages);
52    }
53
54    public String name() {
55        return moduleName;
56    }
57
58    public Map<String, Boolean> requires() {
59        return requires;
60    }
61
62    public Map<String, Set<String>> exports() {
63        return exports;
64    }
65
66    public Set<String> packages() {
67        return packages;
68    }
69
70    /**
71     * Tests if this module can read m
72     */
73    public boolean canRead(Module m) {
74        // ## TODO: handle "re-exported=true"
75        // all JDK modules require all modules containing its direct dependences
76        // should not be an issue
77        return requires.containsKey(m.name());
78    }
79
80    /**
81     * Tests if a given fully-qualified name is an exported type.
82     */
83    public boolean isExported(String cn) {
84        int i = cn.lastIndexOf('.');
85        String pn = i > 0 ? cn.substring(0, i) : "";
86
87        return isExportedPackage(pn);
88    }
89
90    /**
91     * Tests if a given package name is exported.
92     */
93    public boolean isExportedPackage(String pn) {
94        return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false;
95    }
96
97    /**
98     * Tests if the given classname is accessible to module m
99     */
100    public boolean isAccessibleTo(String classname, Module m) {
101        int i = classname.lastIndexOf('.');
102        String pn = i > 0 ? classname.substring(0, i) : "";
103        if (!packages.contains(pn)) {
104            throw new IllegalArgumentException(classname + " is not a member of module " + name());
105        }
106
107        if (m != null && !m.canRead(this)) {
108            trace("%s not readable by %s%n", this.name(), m.name());
109            return false;
110        }
111
112        // exported API
113        Set<String> ms = exports().get(pn);
114        String mname = m != null ? m.name() : "unnamed";
115        if (ms == null) {
116            trace("%s not exported in %s%n", classname, this.name());
117        } else if (!(ms.isEmpty() || ms.contains(mname))) {
118            trace("%s not permit to %s %s%n", classname, mname, ms);
119        }
120        return ms != null && (ms.isEmpty() || ms.contains(mname));
121    }
122
123    private static final boolean traceOn = Boolean.getBoolean("jdeps.debug");
124    private void trace(String fmt, Object... args) {
125        if (traceOn) {
126            System.err.format(fmt, args);
127        }
128    }
129
130    @Override
131    public boolean equals(Object ob) {
132        if (!(ob instanceof Module))
133            return false;
134        Module that = (Module)ob;
135        return (moduleName.equals(that.moduleName)
136                && requires.equals(that.requires)
137                && exports.equals(that.exports)
138                && packages.equals(that.packages));
139    }
140
141    @Override
142    public int hashCode() {
143        int hc = moduleName.hashCode();
144        hc = hc * 43 + requires.hashCode();
145        hc = hc * 43 + exports.hashCode();
146        hc = hc * 43 + packages.hashCode();
147        return hc;
148    }
149
150    @Override
151    public String toString() {
152        return name();
153    }
154
155    public final static class Builder {
156        String name;
157        ClassFileReader reader;
158        final Map<String, Boolean> requires = new HashMap<>();
159        final Map<String, Set<String>> exports = new HashMap<>();
160        final Set<String> packages = new HashSet<>();
161
162        public Builder() {
163        }
164
165        public Builder name(String n) {
166            name = n;
167            return this;
168        }
169
170        public Builder require(String d, boolean reexport) {
171         //   System.err.format("%s depend %s reexports %s%n", name, d, reexport);
172            requires.put(d, reexport);
173            return this;
174        }
175
176        public Builder packages(Set<String> pkgs) {
177            packages.addAll(pkgs);
178            return this;
179        }
180
181        public Builder export(String p, Set<String> ms) {
182            Objects.requireNonNull(p);
183            Objects.requireNonNull(ms);
184            exports.put(p, new HashSet<>(ms));
185            return this;
186        }
187        public Builder classes(ClassFileReader.ModuleClassReader reader) {
188            this.reader = reader;
189            return this;
190        }
191
192        public Module build() {
193            Module m = new Module(reader, name, requires, exports, packages);
194            return m;
195        }
196    }
197}
198