/* * Copyright (c) 2014, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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. */ package com.sun.tools.jdeps; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; /** * JDeps internal representation of module for dependency analysis. */ final class Module extends Archive { private final String moduleName; private final Map requires; private final Map> exports; private final Set packages; private Module(ClassFileReader reader, String name, Map requires, Map> exports, Set packages) { super(name, reader); this.moduleName = name; this.requires = Collections.unmodifiableMap(requires); this.exports = Collections.unmodifiableMap(exports); this.packages = Collections.unmodifiableSet(packages); } public String name() { return moduleName; } public Map requires() { return requires; } public Map> exports() { return exports; } public Set packages() { return packages; } /** * Tests if this module can read m */ public boolean canRead(Module m) { // ## TODO: handle "re-exported=true" // all JDK modules require all modules containing its direct dependences // should not be an issue return requires.containsKey(m.name()); } /** * Tests if a given fully-qualified name is an exported type. */ public boolean isExported(String cn) { int i = cn.lastIndexOf('.'); String pn = i > 0 ? cn.substring(0, i) : ""; return isExportedPackage(pn); } /** * Tests if a given package name is exported. */ public boolean isExportedPackage(String pn) { return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false; } /** * Tests if the given classname is accessible to module m */ public boolean isAccessibleTo(String classname, Module m) { int i = classname.lastIndexOf('.'); String pn = i > 0 ? classname.substring(0, i) : ""; if (!packages.contains(pn)) { throw new IllegalArgumentException(classname + " is not a member of module " + name()); } if (m != null && !m.canRead(this)) { trace("%s not readable by %s%n", this.name(), m.name()); return false; } // exported API Set ms = exports().get(pn); String mname = m != null ? m.name() : "unnamed"; if (ms == null) { trace("%s not exported in %s%n", classname, this.name()); } else if (!(ms.isEmpty() || ms.contains(mname))) { trace("%s not permit to %s %s%n", classname, mname, ms); } return ms != null && (ms.isEmpty() || ms.contains(mname)); } private static final boolean traceOn = Boolean.getBoolean("jdeps.debug"); private void trace(String fmt, Object... args) { if (traceOn) { System.err.format(fmt, args); } } @Override public boolean equals(Object ob) { if (!(ob instanceof Module)) return false; Module that = (Module)ob; return (moduleName.equals(that.moduleName) && requires.equals(that.requires) && exports.equals(that.exports) && packages.equals(that.packages)); } @Override public int hashCode() { int hc = moduleName.hashCode(); hc = hc * 43 + requires.hashCode(); hc = hc * 43 + exports.hashCode(); hc = hc * 43 + packages.hashCode(); return hc; } @Override public String toString() { return name(); } public final static class Builder { String name; ClassFileReader reader; final Map requires = new HashMap<>(); final Map> exports = new HashMap<>(); final Set packages = new HashSet<>(); public Builder() { } public Builder name(String n) { name = n; return this; } public Builder require(String d, boolean reexport) { // System.err.format("%s depend %s reexports %s%n", name, d, reexport); requires.put(d, reexport); return this; } public Builder packages(Set pkgs) { packages.addAll(pkgs); return this; } public Builder export(String p, Set ms) { Objects.requireNonNull(p); Objects.requireNonNull(ms); exports.put(p, new HashSet<>(ms)); return this; } public Builder classes(ClassFileReader.ModuleClassReader reader) { this.reader = reader; return this; } public Module build() { Module m = new Module(reader, name, requires, exports, packages); return m; } } }