--- old/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java 2017-02-07 13:13:27.346826752 +0000 +++ new/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java 2017-02-07 13:13:27.176815077 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2017, 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 @@ -37,11 +37,13 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.TreeSet; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -56,18 +58,37 @@ /** * A module descriptor. * - *

A {@code ModuleDescriptor} is typically created from the binary form - * of a module declaration. Alternatively, the {@link ModuleDescriptor.Builder} - * class can be used to create a {@code ModuleDescriptor} from its components. - * The {@link #module module}, {@link #openModule openModule}, and {@link - * #automaticModule automaticModule} methods create builders for building - * different kinds of modules.

+ *

A module descriptor describes a named module and defines methods to + * obtain each of its components. The module descriptor for a named module + * in the Java virtual machine is obtained by invoking the {@link + * java.lang.reflect.Module Module}'s {@link java.lang.reflect.Module#getDescriptor + * getDescriptor} method. Module descriptors can also be created using the + * {@link ModuleDescriptor.Builder} class or by reading the binary form of a + * module declaration ({@code module-info.class}) using the {@link + * #read(InputStream,Supplier) read} methods defined here.

+ * + *

A module descriptor describes a normal, open, or automatic + * module. Normal modules and open modules describe their {@link + * #requires() dependences}, {@link #exports() exported-packages}, the services + * that they {@link #uses() use} or {@link #provides() provide}, and other + * components. Normal modules may {@link #opens() open} specific + * packages. The module descriptor for an open modules does not declare any + * open packages (its {@code opens} method returns an empty set) but when + * instantiated in the Java virtual machine then it is treated as if all + * packages are open. The module descriptor for an automatic module does not + * declare any dependences (except for the mandatory dependency on {@code + * java.base}), and does not declare any exported or open packages. Automatic + * module receive special treatment during resolution so that they read all + * other modules in the configuration. When an automatic module is instantiated + * in the Java virtual machine then it reads every unnamed module and is + * treated as if all packages are exported and open.

* *

{@code ModuleDescriptor} objects are immutable and safe for use by * multiple concurrent threads.

* - * @since 9 * @see java.lang.reflect.Module + * @since 9 + * @spec JPMS */ public class ModuleDescriptor @@ -75,10 +96,45 @@ { /** + * A modifier on a module. + * + * @see ModuleDescriptor#modifiers() + * @since 9 + */ + public static enum Modifier { + /** + * An open module. An open module does not declare any open packages + * but the resulting module is treated as if all packages are open. + */ + OPEN, + + /** + * An automatic module. An automatic module is treated as if it exports + * and opens all packages. + * + * @apiNote This modifier does not correspond to a module flag in the + * binary form of a module declaration ({@code module-info.class}). + */ + AUTOMATIC, + + /** + * The module was not explicitly or implicitly declared. + */ + SYNTHETIC, + + /** + * The module was implicitly declared. + */ + MANDATED; + } + + + /** *

A dependence upon a module

* * @see ModuleDescriptor#requires() * @since 9 + * @spec JPMS */ public final static class Requires @@ -88,7 +144,9 @@ /** * A modifier on a module dependence. * + * @see Requires#modifiers() * @since 9 + * @spec JPMS */ public static enum Modifier { @@ -171,14 +229,18 @@ * Compares this module dependence to another. * *

Two {@code Requires} objects are compared by comparing their - * module name lexicographically. Where the module names are equal then - * the sets of modifiers are compared based on a value computed from the - * ordinal of each modifier. Where the module names are equal and the - * set of modifiers are equal then the version of the modules recorded - * at compile-time are compared. When comparing the versions recorded - * at compile-time then a dependence that has a recorded version is - * considered to succeed a dependence that does not have a recorded - * version.

+ * module names lexicographically. Where the module names are equal + * then the sets of modifiers are compared in the same way that + * module modifiers are compared (see {@link ModuleDescriptor#compareTo + * ModuleDescriptor.compareTo}). Where the module names are equal and + * the set of modifiers are equal then the version of the modules + * recorded at compile-time are compared. When comparing the versions + * recorded at compile-time then a dependence that has a recorded + * version is considered to succeed a dependence that does not have a + * recorded version.

+ * + * @param that + * The module dependence to compare * * @return A negative integer, zero, or a positive integer if this module * dependence is less than, equal to, or greater than the given @@ -186,39 +248,22 @@ */ @Override public int compareTo(Requires that) { + if (this == that) return 0; + int c = this.name().compareTo(that.name()); - if (c != 0) - return c; + if (c != 0) return c; // modifiers - c = Long.compare(this.modsValue(), that.modsValue()); - if (c != 0) - return c; + long v1 = modsValue(this.modifiers()); + long v2 = modsValue(that.modifiers()); + c = Long.compare(v1, v2); + if (c != 0) return c; // compiledVersion - if (this.compiledVersion != null) { - if (that.compiledVersion != null) - c = this.compiledVersion.compareTo(that.compiledVersion); - else - c = 1; - } else { - if (that.compiledVersion != null) - c = -1; - } - - return c; - } + c = compare(this.compiledVersion, that.compiledVersion); + if (c != 0) return c; - /** - * Return a value for the modifiers to allow sets of modifiers to be - * compared. - */ - private long modsValue() { - long value = 0; - for (Modifier m : mods) { - value += 1 << m.ordinal(); - } - return value; + return 0; } /** @@ -266,9 +311,9 @@ } /** - * Returns a string describing module dependence. + * Returns a string describing this module dependence. * - * @return A string describing module dependence + * @return A string describing this module dependence */ @Override public String toString() { @@ -285,18 +330,23 @@ /** - *

A module export, may be qualified or unqualified.

+ *

A package exported by a module, may be qualified or unqualified.

* * @see ModuleDescriptor#exports() * @since 9 + * @spec JPMS */ - public final static class Exports { + public final static class Exports + implements Comparable + { /** - * A modifier on a module export. + * A modifier on an exported package. * + * @see Exports#modifiers() * @since 9 + * @spec JPMS */ public static enum Modifier { @@ -381,6 +431,51 @@ } /** + * Compares this module export to another. + * + *

Two {@code Exports} objects are compared by comparing the package + * names lexicographically. Where the packages names are equal then the + * sets of modifiers are compared in the same way that module modifiers + * are compared (see {@link ModuleDescriptor#compareTo + * ModuleDescriptor.compareTo}). Where the package names are equal and + * the set of modifiers are equal then the set of target modules are + * compared. This is done by sorting the sets, iterating over both sets + * in ascending order, and comparing the corresponding elements + * lexicographically. Where the sets differ in size, and the larger set + * contains all elements of the smaller set, then the larger set is + * considered to succeed the smaller set.

+ * + * @param that + * The module export to compare + * + * @return A negative integer, zero, or a positive integer if this module + * export is less than, equal to, or greater than the given + * export dependence + */ + @Override + public int compareTo(Exports that) { + if (this == that) return 0; + + int c = source.compareTo(that.source); + if (c != 0) + return c; + + // modifiers + long v1 = modsValue(this.modifiers()); + long v2 = modsValue(that.modifiers()); + c = Long.compare(v1, v2); + if (c != 0) + return c; + + // targets + c = compare(targets, that.targets); + if (c != 0) + return c; + + return 0; + } + + /** * Computes a hash code for this module export. * *

The hash code is based upon the modifiers, the package name, @@ -425,9 +520,9 @@ } /** - * Returns a string describing module export. + * Returns a string describing the exported package. * - * @return A string describing module export + * @return A string describing the exported package */ @Override public String toString() { @@ -441,8 +536,7 @@ /** - *

Represents a module opens directive, may be qualified or - * unqualified.

+ *

A package opened by a module, may be qualified or unqualified.

* *

The opens directive in a module declaration declares a * package to be open to allow all types in the package, and all their @@ -452,26 +546,30 @@ * * @see ModuleDescriptor#opens() * @since 9 + * @spec JPMS */ - public final static class Opens { - + public final static class Opens + implements Comparable + { /** - * A modifier on a module opens directive. + * A modifier on an open package. * + * @see Opens#modifiers() * @since 9 + * @spec JPMS */ public static enum Modifier { /** - * The opens was not explicitly or implicitly declared in the - * source of the module declaration. + * The open package was not explicitly or implicitly declared in + * the source of the module declaration. */ SYNTHETIC, /** - * The opens was implicitly declared in the source of the module - * declaration. + * The open package was implicitly declared in the source of the + * module declaration. */ MANDATED; @@ -544,6 +642,51 @@ } /** + * Compares this module opens to another. + * + *

Two {@code Opens} objects are compared by comparing the package + * names lexicographically. Where the packages names are equal then the + * sets of modifiers are compared in the same way that module modifiers + * are compared (see {@link ModuleDescriptor#compareTo + * ModuleDescriptor.compareTo}). Where the package names are equal and + * the set of modifiers are equal then the set of target modules are + * compared. This is done by sorting the sets, iterating over both sets + * in ascending order, and comparing the corresponding elements + * lexicographically. Where the sets differ in size, and the larger set + * contains all elements of the smaller set, then the larger set is + * considered to succeed the smaller set.

+ * + * @param that + * The module opens to compare + * + * @return A negative integer, zero, or a positive integer if this module + * opens is less than, equal to, or greater than the given + * module opens + */ + @Override + public int compareTo(Opens that) { + if (this == that) return 0; + + int c = source.compareTo(that.source); + if (c != 0) + return c; + + // modifiers + long v1 = modsValue(this.modifiers()); + long v2 = modsValue(that.modifiers()); + c = Long.compare(v1, v2); + if (c != 0) + return c; + + // targets + c = compare(targets, that.targets); + if (c != 0) + return c; + + return 0; + } + + /** * Computes a hash code for this module opens. * *

The hash code is based upon the modifiers, the package name, @@ -588,9 +731,9 @@ } /** - * Returns a string describing module opens. + * Returns a string describing the open package. * - * @return A string describing module opens + * @return A string describing the open package */ @Override public String toString() { @@ -608,10 +751,12 @@ * * @see ModuleDescriptor#provides() * @since 9 + * @spec JPMS */ - public final static class Provides { - + public final static class Provides + implements Comparable + { private final String service; private final List providers; @@ -642,6 +787,46 @@ public List providers() { return providers; } /** + * Compares this provides to another. + * + *

Two {@code Provides} objects are compared by comparing the fully + * qualified class name of the service type lexicographically. Where the + * class names are equal then the list of the provider class names are + * compared by comparing the corresponding elements of both lists + * lexicographically and in sequence. Where the lists differ in size, + * {@code N} is the size of the shorter list, and the first {@code N} + * corresponding elements are equal, then the longer list is considered + * to succeed the shorter list.

+ * + * @param that + * The {@code Provides} to compare + * + * @return A negative integer, zero, or a positive integer if this provides + * is less than, equal to, or greater than the given provides + */ + public int compareTo(Provides that) { + if (this == that) return 0; + + int c = service.compareTo(that.service); + if (c != 0) return c; + + // compare provider class names in sequence + int size1 = this.providers.size(); + int size2 = that.providers.size(); + for (int index=0; index size2) ? 1 : -1; + } + } + + /** * Computes a hash code for this provides. * *

The hash code is based upon the service type and the set of @@ -699,7 +884,7 @@ * *

A version string has three components: The version number itself, an * optional pre-release version, and an optional build version. Each - * component is sequence of tokens; each token is either a non-negative + * component is a sequence of tokens; each token is either a non-negative * integer or a string. Tokens are separated by the punctuation characters * {@code '.'}, {@code '-'}, or {@code '+'}, or by transitions from a * sequence of digits to a sequence of characters that are neither digits @@ -740,6 +925,7 @@ * * @see ModuleDescriptor#version() * @since 9 + * @spec JPMS */ public final static class Version @@ -1009,36 +1195,26 @@ } - + private final String name; private final Version version; - private final boolean open; - - // Indicates if synthesised for a JAR file found on the module path - private final boolean automatic; - - // Not generated from a module-info.java - private final boolean synthetic; - + private final Set modifiers; + private final boolean open; // true if modifiers contains OPEN + private final boolean automatic; // true if modifiers contains AUTOMATIC private final Set requires; private final Set exports; private final Set opens; private final Set uses; private final Set provides; - - // Added post-compilation by tools private final Set packages; private final String mainClass; private final String osName; private final String osArch; private final String osVersion; - private ModuleDescriptor(String name, Version version, - boolean open, - boolean automatic, - boolean synthetic, + Set modifiers, Set requires, Set exports, Set opens, @@ -1052,10 +1228,9 @@ { this.name = name; this.version = version; - this.open = open; - this.automatic = automatic; - this.synthetic = synthetic; - + this.modifiers = emptyOrUnmodifiableSet(modifiers); + this.open = modifiers.contains(Modifier.OPEN); + this.automatic = modifiers.contains(Modifier.AUTOMATIC); assert (requires.stream().map(Requires::name).distinct().count() == requires.size()); this.requires = emptyOrUnmodifiableSet(requires); @@ -1072,40 +1247,12 @@ } /** - * Clones the given module descriptor with an augmented set of packages - */ - ModuleDescriptor(ModuleDescriptor md, Set pkgs) { - this.name = md.name; - this.version = md.version; - this.open = md.open; - this.automatic = md.automatic; - this.synthetic = md.synthetic; - - this.requires = md.requires; - this.exports = md.exports; - this.opens = md.opens; - this.uses = md.uses; - this.provides = md.provides; - - Set packages = new HashSet<>(md.packages); - packages.addAll(pkgs); - this.packages = emptyOrUnmodifiableSet(packages); - - this.mainClass = md.mainClass; - this.osName = md.osName; - this.osArch = md.osArch; - this.osVersion = md.osVersion; - } - - /** * Creates a module descriptor from its components. * The arguments are pre-validated and sets are unmodifiable sets. */ ModuleDescriptor(String name, Version version, - boolean open, - boolean automatic, - boolean synthetic, + Set modifiers, Set requires, Set exports, Set opens, @@ -1120,9 +1267,9 @@ boolean unused) { this.name = name; this.version = version; - this.open = open; - this.automatic = automatic; - this.synthetic = synthetic; + this.modifiers = modifiers; + this.open = modifiers.contains(Modifier.OPEN); + this.automatic = modifiers.contains(Modifier.AUTOMATIC); this.requires = requires; this.exports = exports; this.opens = opens; @@ -1137,7 +1284,7 @@ } /** - *

The module name.

+ *

Returns the module name.

* * @return The module name */ @@ -1146,11 +1293,19 @@ } /** + *

Returns the set of module modifiers.

+ * + * @return A possibly-empty unmodifiable set of modifiers + */ + public Set modifiers() { + return modifiers; + } + + /** *

Returns {@code true} if this is an open module.

* - *

An open module does not declare any open packages (the {@link #opens() - * opens} method returns an empty set) but the resulting module is treated - * as if all packages are open.

+ *

This method is equivalent to testing if the set of {@link #modifiers + * modifiers} contains the {@link Modifier#OPEN OPEN} modifier.

* * @return {@code true} if this is an open module */ @@ -1161,12 +1316,8 @@ /** *

Returns {@code true} if this is an automatic module.

* - *

An automatic module is defined implicitly rather than explicitly - * and therefore does not have a module declaration. JAR files located on - * the application module path, or by the {@link ModuleFinder} returned by - * {@link ModuleFinder#of(java.nio.file.Path[]) ModuleFinder.of}, are - * treated as automatic modules if they do have not have a module - * declaration.

+ *

This method is equivalent to testing if the set of {@link #modifiers + * modifiers} contains the {@link Modifier#OPEN AUTOMATIC} modifier.

* * @return {@code true} if this is an automatic module */ @@ -1175,20 +1326,12 @@ } /** - *

Returns {@code true} if this module descriptor was not generated - * from an explicit module declaration ({@code module-info.java}) - * or an implicit module declaration (an {@link #isAutomatic() automatic} - * module).

+ *

Returns the set of module dependences.

* - * @return {@code true} if this module descriptor was not generated by - * an explicit or implicit module declaration - */ - public boolean isSynthetic() { - return synthetic; - } - - /** - *

The dependences of this module.

+ *

The set includes a dependency on "{@code java.base}" when this + * module is not named "{@code java.base}". If this module is an automatic + * module then it does not have a dependency on any module other than + * "{@code java.base}".

* * @return A possibly-empty unmodifiable set of {@link Requires} objects */ @@ -1197,7 +1340,10 @@ } /** - *

The module exports.

+ *

Returns the set of exported packages.

+ * + *

If this module is an automatic module then the set of exports + * is empty.

* * @return A possibly-empty unmodifiable set of exported packages */ @@ -1206,16 +1352,10 @@ } /** - *

The module opens directives.

+ *

Returns the set of open packages.

* - *

Each {@code Opens} object in the set represents a package (and - * the set of target module names when qualified) where all types in the - * package, and all their members, not just public types and their public - * members, can be reflected on when using APIs that bypass or suppress - * default Java language access control checks.

- * - *

This method returns an empty set when invoked on {@link #isOpen() - * open} module.

+ *

If this module is an open module or an automatic module then the + * set of open packages is empty.

* * @return A possibly-empty unmodifiable set of open packages */ @@ -1224,7 +1364,10 @@ } /** - *

The service dependences of this module.

+ *

Returns the set of service dependences.

+ * + *

If this module is an automatic module then the set of service + * dependences is empty.

* * @return A possibly-empty unmodifiable set of the fully qualified class * names of the service types used @@ -1234,7 +1377,7 @@ } /** - *

The services that this module provides.

+ *

Returns the set of services that the module provides.

* * @return The possibly-empty unmodifiable set of the services that this * module provides @@ -1244,7 +1387,7 @@ } /** - * Returns this module's version. + *

Returns the module version.

* * @return This module's version */ @@ -1253,10 +1396,10 @@ } /** - * Returns a string containing this module's name and, if present, its - * version. + *

Returns a string containing the module name and, if present, its + * version.

* - * @return A string containing this module's name and, if present, its + * @return A string containing the module name and, if present, its * version. */ public String toNameAndVersion() { @@ -1268,51 +1411,51 @@ } /** - * Returns the module's main class. + *

Returns the module main class.

* - * @return The fully qualified class name of this module's main class + * @return The fully qualified class name of the module's main class */ public Optional mainClass() { return Optional.ofNullable(mainClass); } /** - * Returns the operating system name if this module is operating system + * Returns the operating system name if the module is operating system * specific. * * @return The operating system name or an empty {@code Optional} - * if this module is not operating system specific + * if the module is not operating system specific */ public Optional osName() { return Optional.ofNullable(osName); } /** - * Returns the operating system architecture if this module is operating + * Returns the operating system architecture if the module is operating * system architecture specific. * * @return The operating system architecture or an empty {@code Optional} - * if this module is not operating system architecture specific + * if the module is not operating system architecture specific */ public Optional osArch() { return Optional.ofNullable(osArch); } /** - * Returns the operating system version if this module is operating + * Returns the operating system version if the module is operating * system version specific. * * @return The operating system version or an empty {@code Optional} - * if this module is not operating system version specific + * if the module is not operating system version specific */ public Optional osVersion() { return Optional.ofNullable(osVersion); } /** - * Returns the names of all packages in this module. + * Returns the set of packages in the module. * - * @return A possibly-empty unmodifiable set of all packages in the module + * @return A possibly-empty unmodifiable set of the packages in the module */ public Set packages() { return packages; @@ -1320,37 +1463,53 @@ /** - * A builder used for building {@link ModuleDescriptor} objects. + * A builder for building {@link ModuleDescriptor} objects. * - *

{@code ModuleDescriptor} defines the {@link #module module}, {@link - * #openModule openModule}, and {@link #automaticModule automaticModule} - * methods to create builders for building different kinds of modules.

+ *

{@code ModuleDescriptor} defines the {@link #newModule newModule}, + * {@link #newOpenModule newOpenModule}, and {@link #newAutomaticModule + * newAutomaticModule} methods to create builders for building + * normal, open, and automatic modules.

+ * + *

The set of packages in the module are accumulated by the {@code + * Builder} as the {@link ModuleDescriptor.Builder#exports(String) exports}, + * {@link ModuleDescriptor.Builder#opens(String) opens}, + * {@link ModuleDescriptor.Builder#packages(Set) packages}, + * {@link ModuleDescriptor.Builder#provides(String,List) provides}, and + * {@link ModuleDescriptor.Builder#mainClass(String) mainClass} methods are + * invoked.

+ * + *

The module names, package names, and class names that are parameters + * specified to the builder methods are the module names, package names, + * and qualified names of classes (in named packages) as defined in the + * The Java™ Language Specification.

* *

Example usage:

- *
{@code    ModuleDescriptor descriptor = ModuleDescriptor.module("m1")
-     *         .exports("p")
-     *         .requires("m2")
+     * 
{@code    ModuleDescriptor descriptor = ModuleDescriptor.newModule("stats.core")
+     *         .requires("java.base")
+     *         .exports("org.acme.stats.core.clustering")
+     *         .exports("org.acme.stats.core.regression")
+     *         .packages(Set.of("org.acme.stats.core.internal"))
      *         .build();
      * }
* * @apiNote A {@code Builder} checks the components and invariants as - * components are added to the builder. The rational for this is to detect + * components are added to the builder. The rationale for this is to detect * errors as early as possible and not defer all validation to the - * {@link #build build} method. A {@code Builder} cannot be used to create - * a {@link ModuleDescriptor#isSynthetic() synthetic} module. + * {@link #build build} method. * * @since 9 + * @spec JPMS */ public static final class Builder { final String name; - final boolean strict; // true if module names are checked + final boolean strict; + final Set modifiers; final boolean open; - final boolean synthetic; - boolean automatic; + final boolean automatic; + final Set packages = new HashSet<>(); final Map requires = new HashMap<>(); final Map exports = new HashMap<>(); final Map opens = new HashMap<>(); - final Set concealedPackages = new HashSet<>(); final Set uses = new HashSet<>(); final Map provides = new HashMap<>(); Version version; @@ -1362,35 +1521,25 @@ /** * Initializes a new builder with the given module name. * - * @param strict - * Indicates whether module names are checked or not + * If {@code strict} is {@code true} then module, package, and class + * names are checked to ensure they are legal names. In addition, the + * {@link #build buid} method will add "{@code requires java.base}" if + * the dependency is not declared. */ - Builder(String name, boolean strict, boolean open, boolean synthetic) { + Builder(String name, boolean strict, Set modifiers) { this.name = (strict) ? requireModuleName(name) : name; this.strict = strict; - this.open = open; - this.synthetic = synthetic; - } - - /* package */ Builder automatic(boolean automatic) { - this.automatic = automatic; - return this; + this.modifiers = modifiers; + this.open = modifiers.contains(Modifier.OPEN); + this.automatic = modifiers.contains(Modifier.AUTOMATIC); + assert !open || !automatic; } /** - * Returns the set of packages that are exported (unconditionally or - * unconditionally). + * Returns a snapshot of the packages in the module. */ - /* package */ Set exportedPackages() { - return exports.keySet(); - } - - /** - * Returns the set of packages that are opened (unconditionally or - * unconditionally). - */ - /* package */Set openPackages() { - return opens.keySet(); + /* package */ Set packages() { + return Collections.unmodifiableSet(packages); } /** @@ -1406,8 +1555,12 @@ * initialized to build * @throws IllegalStateException * If the dependence on the module has already been declared + * or this builder is for an automatic module */ public Builder requires(Requires req) { + if (automatic) + throw new IllegalStateException("Automatic modules cannot declare" + + " dependences"); String mn = req.name(); if (name.equals(mn)) throw new IllegalArgumentException("Dependence on self"); @@ -1433,11 +1586,12 @@ * @return This builder * * @throws IllegalArgumentException - * If the module name is {@code null}, is not a legal Java - * identifier, or is equal to the module name that this builder + * If the module name is {@code null}, is not a legal module + * name, or is equal to the module name that this builder * was initialized to build * @throws IllegalStateException * If the dependence on the module has already been declared + * or this builder is for an automatic module */ public Builder requires(Set ms, String mn, @@ -1448,6 +1602,23 @@ return requires(new Requires(ms, mn, compiledVersion)); } + /* package */Builder requires(Set ms, + String mn, + String compiledVersion) { + Version v = null; + try { + v = Version.parse(compiledVersion); + } catch (IllegalArgumentException e) { + // for now, drop un-parsable version when non-strict + if (strict) throw e; + } + if (v == null) { + return requires(ms, mn); + } else { + return requires(ms, mn, v); + } + } + /** * Adds a dependence on a module with the given (and possibly empty) * set of modifiers. @@ -1460,11 +1631,12 @@ * @return This builder * * @throws IllegalArgumentException - * If the module name is {@code null}, is not a legal Java - * identifier, or is equal to the module name that this builder + * If the module name is {@code null}, is not a legal module + * name, or is equal to the module name that this builder * was initialized to build * @throws IllegalStateException * If the dependence on the module has already been declared + * or this builder is for an automatic module */ public Builder requires(Set ms, String mn) { if (strict) @@ -1481,18 +1653,19 @@ * @return This builder * * @throws IllegalArgumentException - * If the module name is {@code null}, is not a legal Java - * identifier, or is equal to the module name that this builder + * If the module name is {@code null}, is not a legal module + * name, or is equal to the module name that this builder * was initialized to build * @throws IllegalStateException * If the dependence on the module has already been declared + * or this builder is for an automatic module */ public Builder requires(String mn) { return requires(EnumSet.noneOf(Requires.Modifier.class), mn); } /** - * Adds an export. + * Adds an exported package. * * @param e * The export @@ -1500,29 +1673,27 @@ * @return This builder * * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the {@link Exports#source package} is already declared as + * exported or this builder is for an automatic module */ public Builder exports(Exports e) { - // can't be exported and concealed - String source = e.source(); - if (concealedPackages.contains(source)) { - throw new IllegalStateException("Package " + source - + " already declared"); + if (automatic) { + throw new IllegalStateException("Automatic modules cannot declare" + + " exported packages"); } + String source = e.source(); if (exports.containsKey(source)) { throw new IllegalStateException("Exported package " + source + " already declared"); } - exports.put(source, e); + packages.add(source); return this; } /** - * Adds an export, with the given (and possibly empty) set of modifiers, - * to export a package to a set of target modules. + * Adds an exported package with the given (and possibly empty) set of + * modifiers. The package is exported to a set of target modules. * * @param ms * The set of modifiers @@ -1534,33 +1705,34 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name or any of the target modules is {@code - * null} or is not a legal Java identifier, or the set of - * targets is empty + * If the package name is {@code null} or is not a legal + * package name, the set of target modules is empty, or the set + * of target modules contains a name that is not a legal module + * name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the package is already declared as exported + * or this builder is for an automatic module */ public Builder exports(Set ms, String pn, Set targets) { - Exports e = new Exports(ms, requirePackageName(pn), targets); + Exports e = new Exports(ms, pn, targets); // check targets targets = e.targets(); if (targets.isEmpty()) throw new IllegalArgumentException("Empty target set"); - if (strict) + if (strict) { + requirePackageName(e.source()); targets.stream().forEach(Checks::requireModuleName); - + } return exports(e); } /** - * Adds an unqualified export with the given (and possibly empty) set - * of modifiers. + * Adds an exported package with the given (and possibly empty) set of + * modifiers. The package is exported to all modules. * * @param ms * The set of modifiers @@ -1570,20 +1742,23 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name is {@code null} or is not a legal Java - * identifier + * If the package name is {@code null} or is not a legal + * package name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the package is already declared as exported + * or this builder is for an automatic module */ public Builder exports(Set ms, String pn) { - Exports e = new Exports(ms, requirePackageName(pn), Collections.emptySet()); + if (strict) { + requirePackageName(pn); + } + Exports e = new Exports(ms, pn, Collections.emptySet()); return exports(e); } /** - * Adds an export to export a package to a set of target modules. + * Adds an exported package. The package is exported to a set of target + * modules. * * @param pn * The package name @@ -1593,20 +1768,20 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name or any of the target modules is {@code - * null} or is not a legal Java identifier, or the set of - * targets is empty + * If the package name is {@code null} or is not a legal + * package name, the set of target modules is empty, or the set + * of target modules contains a name that is not a legal module + * name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the package is already declared as exported + * or this builder is for an automatic module */ public Builder exports(String pn, Set targets) { return exports(Collections.emptySet(), pn, targets); } /** - * Adds an unqualified export. + * Adds an exported package. The package is exported to all modules. * * @param pn * The package name @@ -1614,19 +1789,18 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name is {@code null} or is not a legal Java - * identifier + * If the package name is {@code null} or is not a legal + * package name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the package is already declared as exported + * or this builder is for an automatic module */ public Builder exports(String pn) { return exports(Collections.emptySet(), pn); } /** - * Adds an opens directive. + * Adds an open package. * * @param obj * The {@code Opens} object @@ -1634,35 +1808,28 @@ * @return This builder * * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(Opens obj) { - if (open) { - throw new IllegalStateException("open modules cannot declare" - + " open packages"); + if (open || automatic) { + throw new IllegalStateException("Open or automatic modules cannot" + + " declare open packages"); } - - // can't be open and concealed String source = obj.source(); - if (concealedPackages.contains(source)) { - throw new IllegalStateException("Package " + source - + " already declared"); - } if (opens.containsKey(source)) { throw new IllegalStateException("Open package " + source + " already declared"); } - opens.put(source, obj); + packages.add(source); return this; } /** - * Adds an opens directive, with the given (and possibly empty) - * set of modifiers, to open a package to a set of target modules. + * Adds an open package with the given (and possibly empty) set of + * modifiers. The package is open to a set of target modules. * * @param ms * The set of modifiers @@ -1674,33 +1841,34 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name or any of the target modules is {@code - * null} or is not a legal Java identifier, or the set of - * targets is empty + * If the package name is {@code null} or is not a legal + * package name, the set of target modules is empty, or the set + * of target modules contains a name that is not a legal module + * name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(Set ms, String pn, Set targets) { - Opens e = new Opens(ms, requirePackageName(pn), targets); + Opens opens = new Opens(ms, pn, targets); // check targets - targets = e.targets(); + targets = opens.targets(); if (targets.isEmpty()) throw new IllegalArgumentException("Empty target set"); - if (strict) + if (strict) { + requirePackageName(opens.source()); targets.stream().forEach(Checks::requireModuleName); - - return opens(e); + } + return opens(opens); } /** - * Adds an opens directive to open a package with the given (and - * possibly empty) set of modifiers. + * Adds an open package with the given (and possibly empty) set of + * modifiers. The package is open to all modules. * * @param ms * The set of modifiers @@ -1710,21 +1878,22 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name is {@code null} or is not a legal Java - * identifier + * If the package name is {@code null} or is not a legal + * package name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(Set ms, String pn) { - Opens e = new Opens(ms, requirePackageName(pn), Collections.emptySet()); + if (strict) { + requirePackageName(pn); + } + Opens e = new Opens(ms, pn, Collections.emptySet()); return opens(e); } /** - * Adds an opens directive to open a package to a set of target - * modules. + * Adds an open package. The package is open to a set of target modules. * * @param pn * The package name @@ -1734,20 +1903,20 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name or any of the target modules is {@code - * null} or is not a legal Java identifier, or the set of - * targets is empty + * If the package name is {@code null} or is not a legal + * package name, the set of target modules is empty, or the set + * of target modules contains a name that is not a legal module + * name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(String pn, Set targets) { return opens(Collections.emptySet(), pn, targets); } /** - * Adds an opens directive to open a package. + * Adds an open package. The package is open to all modules. * * @param pn * The package name @@ -1755,12 +1924,11 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name is {@code null} or is not a legal Java - * identifier + * If the package name is {@code null} or is not a legal + * package name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(String pn) { return opens(Collections.emptySet(), pn); @@ -1775,12 +1943,16 @@ * @return This builder * * @throws IllegalArgumentException - * If the service type is {@code null} or is not a legal Java - * identifier + * If the service type is {@code null} or not a qualified name of + * a class in a named package * @throws IllegalStateException * If a dependency on the service type has already been declared + * or this is a builder for an an automatic module */ public Builder uses(String service) { + if (automatic) + throw new IllegalStateException("Automatic modules can not declare" + + " service dependences"); if (uses.contains(requireServiceTypeName(service))) throw new IllegalStateException("Dependence upon service " + service + " already declared"); @@ -1789,7 +1961,9 @@ } /** - * Provides a service with one or more implementations. + * Provides a service with one or more implementations. The package for + * each {@link Provides#providers provider} (or provider factory) is + * added to the module if not already added. * * @param p * The provides @@ -1801,16 +1975,18 @@ * declared */ public Builder provides(Provides p) { - String st = p.service(); - if (provides.containsKey(st)) + String service = p.service(); + if (provides.containsKey(service)) throw new IllegalStateException("Providers of service " - + st + " already declared"); - provides.put(st, p); + + service + " already declared"); + provides.put(service, p); + p.providers().forEach(name -> packages.add(packageName(name))); return this; } /** - * Provides implementations of a service. + * Provides implementations of a service. The package for each provider + * (or provider factory) is added to the module if not already added. * * @param service * The service type @@ -1821,103 +1997,59 @@ * * @throws IllegalArgumentException * If the service type or any of the provider class names is - * {@code null} or is not a legal Java identifier, or the list - * of provider class names is empty + * {@code null} or not a qualified name of a class in a named + * package, or the list of provider class names is empty * @throws IllegalStateException * If the providers for the service type have already been * declared */ public Builder provides(String service, List providers) { - if (provides.containsKey(service)) - throw new IllegalStateException("Providers of service " - + service + " already declared by " + name); - - Provides p = new Provides(requireServiceTypeName(service), providers); + Provides p = new Provides(service, providers); // check providers after the set has been copied. List providerNames = p.providers(); if (providerNames.isEmpty()) throw new IllegalArgumentException("Empty providers set"); - providerNames.forEach(Checks::requireServiceProviderName); - provides.put(service, p); - return this; - } - - /** - * Provides an implementation of a service. - * - * @param service - * The service type - * @param provider - * The provider or provider factory class name - * - * @return This builder - * - * @throws IllegalArgumentException - * If the service type or the provider class name is {@code - * null} or is not a legal Java identifier - * @throws IllegalStateException - * If the providers for the service type have already been - * declared - */ - public Builder provides(String service, String provider) { - if (provider == null) - throw new IllegalArgumentException("'provider' is null"); - return provides(service, List.of(provider)); + if (strict) { + requireServiceTypeName(p.service()); + providerNames.forEach(Checks::requireServiceProviderName); + } else { + // Disallow service/providers in unnamed package + String pn = packageName(service); + if (pn.isEmpty()) { + throw new IllegalArgumentException(service + + ": unnamed package"); + } + for (String name : providerNames) { + pn = packageName(name); + if (pn.isEmpty()) { + throw new IllegalArgumentException(name + + ": unnamed package"); + } + } + } + return provides(p); } /** - * Adds a (possible empty) set of packages to the module + * Adds packages to the module. All packages in the set of package names + * that are not in the module are added to module. * * @param pns - * The set of package names + * The (possibly empty) set of package names * * @return This builder * * @throws IllegalArgumentException * If any of the package names is {@code null} or is not a - * legal Java identifier - * @throws IllegalStateException - * If any of packages are already declared as packages in - * the module. This includes packages that are already - * declared as exported or open packages. + * legal package name */ - public Builder contains(Set pns) { - pns.forEach(this::contains); - return this; - } - - /** - * Adds a package to the module. - * - * @param pn - * The package name - * - * @return This builder - * - * @throws IllegalArgumentException - * If the package name is {@code null}, or is not a legal Java - * identifier - * @throws IllegalStateException - * If the package is already declared as a package in the - * module. This includes the package already declared as an - * exported or open package. - */ - public Builder contains(String pn) { - Checks.requirePackageName(pn); - if (concealedPackages.contains(pn)) { - throw new IllegalStateException("Package " + pn - + " already declared"); + public Builder packages(Set pns) { + if (strict) { + pns = new HashSet<>(pns); + pns.forEach(Checks::requirePackageName); } - if (exports.containsKey(pn)) { - throw new IllegalStateException("Exported package " - + pn + " already declared"); - } - if (opens.containsKey(pn)) { - throw new IllegalStateException("Open package " - + pn + " already declared"); - } - concealedPackages.add(pn); + this.packages.addAll(pns); return this; } @@ -1937,22 +2069,35 @@ /** * Sets the module version. * - * @param v + * @param vs * The version string to parse * * @return This builder * * @throws IllegalArgumentException - * If {@code v} is null or cannot be parsed as a version string + * If {@code vs} is {@code null} or cannot be parsed as a + * version string * * @see Version#parse(String) */ - public Builder version(String v) { - return version(Version.parse(v)); + public Builder version(String vs) { + Version v; + if (strict) { + v = Version.parse(vs); + } else { + try { + v = Version.parse(vs); + } catch (IllegalArgumentException ignore) { + // for now, ignore when non-strict + return this; + } + } + return version(v); } /** - * Sets the module main class. + * Sets the module main class. The package for the main class is added + * to the module if not already added. * * @param mc * The module main class @@ -1960,10 +2105,24 @@ * @return This builder * * @throws IllegalArgumentException - * If {@code mainClass} is null or is not a legal Java identifier + * If {@code mainClass} is {@code null} or not a qualified + * name of a class in a named package */ public Builder mainClass(String mc) { - mainClass = requireBinaryName("main class name", mc); + String pn; + if (strict) { + mc = requireQualifiedClassName("main class name", mc); + pn = packageName(mc); + assert !pn.isEmpty(); + } else { + // Disallow main class in unnamed package + pn = packageName(mc); + if (pn.isEmpty()) { + throw new IllegalArgumentException(mc + ": unnamed package"); + } + } + mainClass = mc; + packages.add(pn); return this; } @@ -1976,7 +2135,7 @@ * @return This builder * * @throws IllegalArgumentException - * If {@code name} is null or the empty String + * If {@code name} is {@code null} or the empty String */ public Builder osName(String name) { if (name == null || name.isEmpty()) @@ -1994,7 +2153,7 @@ * @return This builder * * @throws IllegalArgumentException - * If {@code name} is null or the empty String + * If {@code name} is {@code null} or the empty String */ public Builder osArch(String arch) { if (arch == null || arch.isEmpty()) @@ -2012,7 +2171,7 @@ * @return This builder * * @throws IllegalArgumentException - * If {@code name} is null or the empty String + * If {@code name} is {@code null} or the empty String */ public Builder osVersion(String version) { if (version == null || version.isEmpty()) @@ -2024,26 +2183,34 @@ /** * Builds and returns a {@code ModuleDescriptor} from its components. * + *

The module will require "{@code java.base}" even if the dependence + * has not been declared (the exception is when building a module named + * "{@code java.base}" as it cannot require itself). The dependence on + * "{@code java.base}" will have the {@link + * java.lang.module.ModuleDescriptor.Requires.Modifier#MANDATED MANDATED} + * modifier if the dependence was not declared.

+ * * @return The module descriptor */ public ModuleDescriptor build() { Set requires = new HashSet<>(this.requires.values()); - - Set packages = new HashSet<>(); - packages.addAll(exports.keySet()); - packages.addAll(opens.keySet()); - packages.addAll(concealedPackages); - Set exports = new HashSet<>(this.exports.values()); Set opens = new HashSet<>(this.opens.values()); + // add dependency on java.base + if (strict + && !name.equals("java.base") + && !this.requires.containsKey("java.base")) { + requires.add(new Requires(Set.of(Requires.Modifier.MANDATED), + "java.base", + null)); + } + Set provides = new HashSet<>(this.provides.values()); return new ModuleDescriptor(name, version, - open, - automatic, - synthetic, + modifiers, requires, exports, opens, @@ -2062,16 +2229,20 @@ * Compares this module descriptor to another. * *

Two {@code ModuleDescriptor} objects are compared by comparing their - * module name lexicographically. Where the module names are equal then - * the versions, if present, are compared.

- * - * @apiNote For now, the natural ordering is not consistent with equals. - * If two module descriptors have equal module names, equal versions if - * present, but their corresponding components are not equal, then they - * will be considered equal by this method. + * module names lexicographically. Where the module names are equal then the + * module versions are compared. When comparing the module versions then a + * module descriptor with a version is considered to succeed a module + * descriptor that does not have a version. Where the module names are equal + * and the versions are equal (or not present in both), then the set of + * modifiers are compared. Sets of modifiers are compared by comparing + * a binary value computed for each set. If a modifier is present + * in the set then the bit at the position of its ordinal is {@code 1} + * in the binary value, otherwise {@code 0}. If the two set of modifiers + * are also equal then the other components of the module descriptors are + * compared in a manner that is consistent with {@code equals}.

* * @param that - * The object to which this module descriptor is to be compared + * The module descriptor to compare * * @return A negative integer, zero, or a positive integer if this module * descriptor is less than, equal to, or greater than the given @@ -2079,16 +2250,50 @@ */ @Override public int compareTo(ModuleDescriptor that) { + if (this == that) return 0; + int c = this.name().compareTo(that.name()); if (c != 0) return c; - if (version == null) { - if (that.version == null) - return 0; - return -1; - } - if (that.version == null) - return +1; - return version.compareTo(that.version); + + c = compare(this.version, that.version); + if (c != 0) return c; + + long v1 = modsValue(this.modifiers()); + long v2 = modsValue(that.modifiers()); + c = Long.compare(v1, v2); + if (c != 0) return c; + + c = compare(this.requires, that.requires); + if (c != 0) return c; + + c = compare(this.packages, that.packages); + if (c != 0) return c; + + c = compare(this.exports, that.exports); + if (c != 0) return c; + + c = compare(this.opens, that.opens); + if (c != 0) return c; + + c = compare(this.uses, that.uses); + if (c != 0) return c; + + c = compare(this.provides, that.provides); + if (c != 0) return c; + + c = compare(this.mainClass, that.mainClass); + if (c != 0) return c; + + c = compare(this.osName, that.osName); + if (c != 0) return c; + + c = compare(this.osArch, that.osArch); + if (c != 0) return c; + + c = compare(this.osVersion, that.osVersion); + if (c != 0) return c; + + return 0; } /** @@ -2115,10 +2320,9 @@ return false; ModuleDescriptor that = (ModuleDescriptor)ob; return (name.equals(that.name) - && open == that.open - && automatic == that.automatic - && synthetic == that.synthetic + && modifiers.equals(that.modifiers) && requires.equals(that.requires) + && Objects.equals(packages, that.packages) && exports.equals(that.exports) && opens.equals(that.opens) && uses.equals(that.uses) @@ -2127,12 +2331,9 @@ && Objects.equals(mainClass, that.mainClass) && Objects.equals(osName, that.osName) && Objects.equals(osArch, that.osArch) - && Objects.equals(osVersion, that.osVersion) - && Objects.equals(packages, that.packages)); + && Objects.equals(osVersion, that.osVersion)); } - private transient int hash; // cached hash code - /** * Computes a hash code for this module descriptor. * @@ -2147,10 +2348,9 @@ int hc = hash; if (hc == 0) { hc = name.hashCode(); - hc = hc * 43 + Boolean.hashCode(open); - hc = hc * 43 + Boolean.hashCode(automatic); - hc = hc * 43 + Boolean.hashCode(synthetic); + hc = hc * 43 + Objects.hashCode(modifiers); hc = hc * 43 + requires.hashCode(); + hc = hc * 43 + Objects.hashCode(packages); hc = hc * 43 + exports.hashCode(); hc = hc * 43 + opens.hashCode(); hc = hc * 43 + uses.hashCode(); @@ -2160,18 +2360,18 @@ hc = hc * 43 + Objects.hashCode(osName); hc = hc * 43 + Objects.hashCode(osArch); hc = hc * 43 + Objects.hashCode(osVersion); - hc = hc * 43 + Objects.hashCode(packages); if (hc == 0) hc = -1; hash = hc; } return hc; } + private transient int hash; // cached hash code /** - * Returns a string describing this descriptor. + *

Returns a string describing the module.

* - * @return A string describing this descriptor + * @return A string describing the module */ @Override public String toString() { @@ -2201,31 +2401,44 @@ * * @param name * The module name + * @param ms + * The set of module modifiers + * + * @return A new builder + * + * @throws IllegalArgumentException + * If the module name is {@code null} or is not a legal module + * name, or the set of modifiers contains both {@link Modifier#OPEN + * OPEN} and {@link Modifier#AUTOMATIC AUTOMATIC} + */ + public static Builder newModule(String name, Set ms) { + Set mods = new HashSet<>(ms); + if (mods.contains(Modifier.OPEN) && mods.contains(Modifier.AUTOMATIC)) + throw new IllegalArgumentException("OPEN and AUTOMATIC not allowed"); + return new Builder(name, true, mods); + } + + /** + * Instantiates a builder to build a module descriptor. + * + * @param name + * The module name * * @return A new builder * * @throws IllegalArgumentException - * If the module name is {@code null} or is not a legal Java - * identifier + * If the module name is {@code null} or is not a legal module + * name */ - public static Builder module(String name) { - return new Builder(name, true, false, false); + public static Builder newModule(String name) { + return new Builder(name, true, Set.of()); } /** * Instantiates a builder to build a module descriptor for an open module. - * An open module does not declare any open packages but the resulting - * module is treated as if all packages are open. * - *

As an example, the following creates a module descriptor for an open - * name "{@code m}" containing two packages, one of which is exported.

- *
{@code
-     *     ModuleDescriptor descriptor = ModuleDescriptor.openModule("m")
-     *         .requires("java.base")
-     *         .exports("p")
-     *         .contains("q")
-     *         .build();
-     * }
+ *

The builder for an open module cannot be used to declare any open + * packages.

* * @param name * The module name @@ -2233,19 +2446,20 @@ * @return A new builder that builds an open module * * @throws IllegalArgumentException - * If the module name is {@code null} or is not a legal Java - * identifier + * If the module name is {@code null} or is not a legal module + * name */ - public static Builder openModule(String name) { - return new Builder(name, true, true, false); + public static Builder newOpenModule(String name) { + return new Builder(name, true, Set.of(Modifier.OPEN)); } /** * Instantiates a builder to build a module descriptor for an automatic - * module. Automatic modules receive special treatment during resolution - * (see {@link Configuration}) so that they read all other modules. When - * Instantiated in the Java virtual machine as a {@link java.lang.reflect.Module} - * then the Module reads every unnamed module in the Java virtual machine. + * module. + * + *

The builder for an automatic module cannot be used to declare module + * or service dependences. It also cannot be used to declare any exported + * or open packages.

* * @param name * The module name @@ -2253,13 +2467,13 @@ * @return A new builder that builds an automatic module * * @throws IllegalArgumentException - * If the module name is {@code null} or is not a legal Java - * identifier + * If the module name is {@code null} or is not a legal module + * name * * @see ModuleFinder#of(Path[]) */ - public static Builder automaticModule(String name) { - return new Builder(name, true, false, false).automatic(true); + public static Builder newAutomaticModule(String name) { + return new Builder(name, true, Set.of(Modifier.AUTOMATIC)); } @@ -2269,8 +2483,12 @@ * *

If the descriptor encoded in the input stream does not indicate a * set of packages in the module then the {@code packageFinder} will be - * invoked. If the {@code packageFinder} throws an {@link UncheckedIOException} - * then {@link IOException} cause will be re-thrown.

+ * invoked. The set of packages that the {@code packageFinder} returns + * must include all the packages that the module exports, opens, as well + * as the packages of the service implementations that the module provides, + * and the package of the main class (if the module has a main class). If + * the {@code packageFinder} throws an {@link UncheckedIOException} then + * {@link IOException} cause will be re-thrown.

* *

If there are bytes following the module descriptor then it is * implementation specific as to whether those bytes are read, ignored, @@ -2292,7 +2510,9 @@ * @return The module descriptor * * @throws InvalidModuleDescriptorException - * If an invalid module descriptor is detected + * If an invalid module descriptor is detected or the set of + * packages returned by the {@code packageFinder} does not include + * all of the packages obtained from the module descriptor * @throws IOException * If an I/O error occurs reading from the input stream or {@code * UncheckedIOException} is thrown by the package finder @@ -2305,8 +2525,12 @@ } /** - * Reads the binary form of a module declaration from an input stream - * as a module descriptor. + * Reads the binary form of a module declaration from an input stream as a + * module descriptor. This method works exactly as specified by the 2-arg + * {@link #read(InputStream,Supplier) read} method with the exception that + * a packager finder is not used to find additional packages when the + * module descriptor read from the stream does not indicate the set of + * packages. * * @param in * The input stream @@ -2327,7 +2551,13 @@ * as a module descriptor. * *

If the descriptor encoded in the byte buffer does not indicate a - * set of packages then the {@code packageFinder} will be invoked.

+ * set of packages in the module then the {@code packageFinder} will be + * invoked. The set of packages that the {@code packageFinder} returns + * must include all the packages that the module exports, opens, as well + * as the packages of the service implementations that the module provides, + * and the package of the main class (if the module has a main class). If + * the {@code packageFinder} throws an {@link UncheckedIOException} then + * {@link IOException} cause will be re-thrown.

* *

The module descriptor is read from the buffer stating at index * {@code p}, where {@code p} is the buffer's {@link ByteBuffer#position() @@ -2353,7 +2583,9 @@ * @return The module descriptor * * @throws InvalidModuleDescriptorException - * If an invalid module descriptor is detected + * If an invalid module descriptor is detected or the set of + * packages returned by the {@code packageFinder} does not include + * all of the packages obtained from the module descriptor */ public static ModuleDescriptor read(ByteBuffer bb, Supplier> packageFinder) @@ -2362,8 +2594,11 @@ } /** - * Reads the binary form of a module declaration from a byte buffer - * as a module descriptor. + * Reads the binary form of a module declaration from a byte buffer as a + * module descriptor. This method works exactly as specified by the 2-arg + * {@link #read(ByteBuffer,Supplier) read} method with the exception that a + * packager finder is not used to find additional packages when the module + * descriptor encoded in the buffer does not indicate the set of packages. * * @param bb * The byte buffer @@ -2398,6 +2633,11 @@ } } + private static String packageName(String cn) { + int index = cn.lastIndexOf('.'); + return (index == -1) ? "" : cn.substring(0, index); + } + /** * Returns a string containing the given set of modifiers and label. */ @@ -2407,6 +2647,46 @@ .collect(Collectors.joining(" ")); } + private static > + int compare(T obj1, T obj2) { + if (obj1 != null) { + return (obj2 != null) ? obj1.compareTo(obj2) : 1; + } else { + return (obj2 == null) ? 0 : -1; + } + } + + /** + * Compares two sets of {@code Comparable} objects. + */ + private static > + int compare(Set s1, Set s2) { + Iterator iterator1 = new TreeSet<>(s1).iterator(); + Iterator iterator2 = new TreeSet<>(s2).iterator(); + while (iterator1.hasNext()) { + if (!iterator2.hasNext()) + return 1; // s1 has more elements + T e1 = iterator1.next(); + T e2 = iterator2.next(); + int c = e1.compareTo(e2); + if (c != 0) + return c; + } + if (iterator2.hasNext()) { + return -1; // s2 has more elements + } else { + return 0; + } + } + + private static > long modsValue(Set set) { + long value = 0; + for (Enum e : set) { + value += 1 << e.ordinal(); + } + return value; + } + static { /** * Setup the shared secret to allow code in other packages access @@ -2417,19 +2697,21 @@ @Override public Builder newModuleBuilder(String mn, boolean strict, - boolean open, - boolean synthetic) { - return new Builder(mn, strict, open, synthetic); + Set modifiers) { + return new Builder(mn, strict, modifiers); } @Override - public Set exportedPackages(ModuleDescriptor.Builder builder) { - return builder.exportedPackages(); + public Set packages(ModuleDescriptor.Builder builder) { + return builder.packages(); } @Override - public Set openPackages(ModuleDescriptor.Builder builder) { - return builder.openPackages(); + public void requires(ModuleDescriptor.Builder builder, + Set ms, + String mn, + String compiledVersion) { + builder.requires(ms, mn, compiledVersion); } @Override @@ -2467,22 +2749,9 @@ } @Override - public Version newVersion(String v) { - return new Version(v); - } - - @Override - public ModuleDescriptor newModuleDescriptor(ModuleDescriptor md, - Set pkgs) { - return new ModuleDescriptor(md, pkgs); - } - - @Override public ModuleDescriptor newModuleDescriptor(String name, Version version, - boolean open, - boolean automatic, - boolean synthetic, + Set modifiers, Set requires, Set exports, Set opens, @@ -2496,9 +2765,7 @@ int hashCode) { return new ModuleDescriptor(name, version, - open, - automatic, - synthetic, + modifiers, requires, exports, opens, @@ -2514,12 +2781,12 @@ } @Override - public Configuration resolveRequiresAndUses(ModuleFinder finder, - Collection roots, - boolean check, - PrintStream traceOutput) + public Configuration resolveAndBind(ModuleFinder finder, + Collection roots, + boolean check, + PrintStream traceOutput) { - return Configuration.resolveRequiresAndUses(finder, roots, check, traceOutput); + return Configuration.resolveAndBind(finder, roots, check, traceOutput); } }); }