langtools/src/jdk.dev/share/classes/com/sun/tools/jdeps/Module.java
author sla
Thu, 13 Nov 2014 07:58:28 +0100
changeset 27622 38913dc333ed
parent 25874 83c19f00452c
child 27579 d1a63c99cdd5
permissions -rw-r--r--
Merge

/*
 * 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<String, Boolean> requires;
    private final Map<String, Set<String>> exports;
    private final Set<String> packages;

    private Module(ClassFileReader reader, String name,
                   Map<String, Boolean> requires,
                   Map<String, Set<String>> exports,
                   Set<String> 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<String, Boolean> requires() {
        return requires;
    }

    public Map<String, Set<String>> exports() {
        return exports;
    }

    public Set<String> 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<String> 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<String, Boolean> requires = new HashMap<>();
        final Map<String, Set<String>> exports = new HashMap<>();
        final Set<String> 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 include(String p) {
            packages.add(p);
            return this;
        }

        public Builder export(String p, Set<String> ms) {
            Objects.requireNonNull(p);
            Objects.requireNonNull(ms);
            exports.put(p, new HashSet<>(ms));
            return this;
        }
        public Builder classes(ClassFileReader reader) {
            this.reader = reader;
            return this;
        }
        public Module build() {
            Module m = new Module(reader, name, requires, exports, packages);
            return m;
        }
    }
}