jdk/src/java.base/share/classes/jdk/internal/module/Builder.java
author alanb
Thu, 01 Dec 2016 08:57:53 +0000
changeset 42338 a60f280f803c
parent 38888 dd584fbea6a2
child 42703 20c39ea4a507
permissions -rw-r--r--
8169069: Module system implementation refresh (11/2016) Reviewed-by: plevart, chegar, psandoz, mchung, alanb, dfuchs, naoto, coffeys, weijun Contributed-by: alan.bateman@oracle.com, mandy.chung@oracle.com, claes.redestad@oracle.com, mark.reinhold@oracle.com

/*
 * Copyright (c) 2015, 2016, 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 jdk.internal.module;

import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Version;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.misc.SharedSecrets;

/**
 * This builder is optimized for reconstituting ModuleDescriptor
 * for system modules.  The validation should be done at jlink time.
 *
 * 1. skip name validation
 * 2. ignores dependency hashes.
 * 3. ModuleDescriptor skips the defensive copy and directly uses the
 *    sets/maps created in this Builder.
 *
 * SystemModules should contain modules for the boot layer.
 */
final class Builder {
    private static final JavaLangModuleAccess jlma =
        SharedSecrets.getJavaLangModuleAccess();

    // Static cache of the most recently seen Version to cheaply deduplicate
    // most Version objects.  JDK modules have the same version.
    static Version cachedVersion;

    /**
     * Returns a {@link Requires} for a dependence on a module
     * with the given (and possibly empty) set of modifiers.
     */
    public static Requires newRequires(Set<Requires.Modifier> mods,
                                       String mn)
    {
        return jlma.newRequires(mods, mn);
    }

    /**
     * Returns a {@link Exports} for a qualified export, with
     * the given (and possibly empty) set of modifiers,
     * to a set of target modules.
     */
    public static Exports newExports(Set<Exports.Modifier> ms,
                                     String pn,
                                     Set<String> targets) {
        return jlma.newExports(ms, pn, targets);
    }

    /**
     * Returns an {@link Opens} for an unqualified open with a given set of
     * modifiers.
     */
    public static Opens newOpens(Set<Opens.Modifier> ms, String pn) {
        return jlma.newOpens(ms, pn);
    }

    /**
     * Returns an {@link Opens} for a qualified opens, with
     * the given (and possibly empty) set of modifiers,
     * to a set of target modules.
     */
    public static Opens newOpens(Set<Opens.Modifier> ms,
                                 String pn,
                                 Set<String> targets) {
        return jlma.newOpens(ms, pn, targets);
    }

    /**
     * Returns a {@link Exports} for an unqualified export with a given set
     * of modifiers.
     */
    public static Exports newExports(Set<Exports.Modifier> ms, String pn) {
        return jlma.newExports(ms, pn);
    }

    /**
     * Returns a {@link Provides} for a service with a given list of
     * implementation classes.
     */
    public static Provides newProvides(String st, List<String> pcs) {
        return jlma.newProvides(st, pcs);
    }

    final String name;
    boolean open;
    boolean automatic;
    boolean synthetic;
    Set<Requires> requires;
    Set<Exports> exports;
    Set<Opens> opens;
    Set<String> packages;
    Set<String> uses;
    Set<Provides> provides;
    Version version;
    String mainClass;
    String osName;
    String osArch;
    String osVersion;
    String algorithm;
    Map<String, byte[]> hashes;

    Builder(String name) {
        this.name = name;
        this.requires = Collections.emptySet();
        this.exports = Collections.emptySet();
        this.opens = Collections.emptySet();
        this.provides = Collections.emptySet();
        this.uses = Collections.emptySet();
    }

    Builder open(boolean value) {
        this.open = value;
        return this;
    }

    Builder automatic(boolean value) {
        this.automatic = value;
        return this;
    }

    Builder synthetic(boolean value) {
        this.synthetic = value;
        return this;
    }

    /**
     * Sets module exports.
     */
    public Builder exports(Exports[] exports) {
        this.exports = Set.of(exports);
        return this;
    }

    /**
     * Sets module opens.
     */
    public Builder opens(Opens[] opens) {
        this.opens = Set.of(opens);
        return this;
    }

    /**
     * Sets module requires.
     */
    public Builder requires(Requires[] requires) {
        this.requires = Set.of(requires);
        return this;
    }

    /**
     * Adds a set of (possible empty) packages.
     */
    public Builder packages(Set<String> packages) {
        this.packages = packages;
        return this;
    }

    /**
     * Sets the set of service dependences.
     */
    public Builder uses(Set<String> uses) {
        this.uses = uses;
        return this;
    }

    /**
     * Sets module provides.
     */
    public Builder provides(Provides[] provides) {
        this.provides = Set.of(provides);
        return this;
    }

    /**
     * Sets the module version.
     *
     * @throws IllegalArgumentException if {@code v} is null or cannot be
     *         parsed as a version string
     * @throws IllegalStateException if the module version is already set
     *
     * @see Version#parse(String)
     */
    public Builder version(String v) {
        if (version != null)
            throw new IllegalStateException("module version already set");
        Version ver = cachedVersion;
        if (ver != null && v.equals(ver.toString())) {
            version = ver;
        } else {
            cachedVersion = version = Version.parse(v);
        }
        return this;
    }

    /**
     * Sets the module main class.
     *
     * @throws IllegalStateException if already set
     */
    public Builder mainClass(String mc) {
        if (mainClass != null)
            throw new IllegalStateException("main class already set");
        mainClass = mc;
        return this;
    }

    /**
     * Sets the OS name.
     *
     * @throws IllegalStateException if already set
     */
    public Builder osName(String name) {
        if (osName != null)
            throw new IllegalStateException("OS name already set");
        this.osName = name;
        return this;
    }

    /**
     * Sets the OS arch.
     *
     * @throws IllegalStateException if already set
     */
    public Builder osArch(String arch) {
        if (osArch != null)
            throw new IllegalStateException("OS arch already set");
        this.osArch = arch;
        return this;
    }

    /**
     * Sets the OS version.
     *
     * @throws IllegalStateException if already set
     */
    public Builder osVersion(String version) {
        if (osVersion != null)
            throw new IllegalStateException("OS version already set");
        this.osVersion = version;
        return this;
    }

    /**
     * Sets the algorithm of the module hashes
     */
    public Builder algorithm(String algorithm) {
        this.algorithm = algorithm;
        return this;
    }

    /**
     * Sets the module hash for the given module name
     */
    public Builder moduleHash(String mn, byte[] hash) {
        if (hashes == null)
            hashes = new HashMap<>();

        hashes.put(mn, hash);
        return this;
    }

    /**
     * Builds a {@code ModuleDescriptor} from the components.
     */
    public ModuleDescriptor build(int hashCode) {
        assert name != null;

        ModuleHashes moduleHashes =
            hashes != null ? new ModuleHashes(algorithm, hashes) : null;

        return jlma.newModuleDescriptor(name,
                                        open,
                                        automatic,
                                        synthetic,
                                        requires,
                                        exports,
                                        opens,
                                        uses,
                                        provides,
                                        version,
                                        mainClass,
                                        osName,
                                        osArch,
                                        osVersion,
                                        packages,
                                        moduleHashes,
                                        hashCode);
    }
}