/*
* Copyright (c) 2009, 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 java.lang.module;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static jdk.internal.module.Checks.*;
import static java.util.Objects.*;
import jdk.internal.module.Checks;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.WarnIfResolvedReason;
/**
* 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.
*
*
{@code ModuleDescriptor} objects are immutable and safe for use by
* multiple concurrent threads.
*
* @since 9
* @see java.lang.reflect.Module
*/
public class ModuleDescriptor
implements Comparable
{
/**
*
A dependence upon a module
*
* @see ModuleDescriptor#requires()
* @since 9
*/
public final static class Requires
implements Comparable
{
/**
* A modifier on a module dependence.
*
* @since 9
*/
public static enum Modifier {
/**
* The dependence causes any module which depends on the current
* module to have an implicitly declared dependence on the module
* named by the {@code Requires}.
*/
TRANSITIVE,
/**
* The dependence is mandatory in the static phase, during compilation,
* but is optional in the dynamic phase, during execution.
*/
STATIC,
/**
* The dependence was not explicitly or implicitly declared in the
* source of the module declaration.
*/
SYNTHETIC,
/**
* The dependence was implicitly declared in the source of the module
* declaration.
*/
MANDATED;
}
private final Set mods;
private final String name;
private Requires(Set ms, String mn) {
if (ms.isEmpty()) {
ms = Collections.emptySet();
} else {
ms = Collections.unmodifiableSet(EnumSet.copyOf(ms));
}
this.mods = ms;
this.name = mn;
}
private Requires(Set ms, String mn, boolean unused) {
this.mods = ms;
this.name = mn;
}
/**
* Returns the set of modifiers.
*
* @return A possibly-empty unmodifiable set of modifiers
*/
public Set modifiers() {
return mods;
}
/**
* Return the module name.
*
* @return The module name
*/
public String name() {
return name;
}
/**
* 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.
*
* @return A negative integer, zero, or a positive integer if this module
* dependence is less than, equal to, or greater than the given
* module dependence
*/
@Override
public int compareTo(Requires that) {
int c = this.name().compareTo(that.name());
if (c != 0)
return c;
// same name, compare by modifiers
return Long.compare(this.modsValue(), that.modsValue());
}
/**
* 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;
}
/**
* Tests this module dependence for equality with the given object.
*
*
If the given object is not a {@code Requires} then this method
* returns {@code false}. Two module dependence objects are equal if
* the module names are equal and set of modifiers are equal.
*
*
This method satisfies the general contract of the {@link
* java.lang.Object#equals(Object) Object.equals} method.
*
* @param ob
* the object to which this object is to be compared
*
* @return {@code true} if, and only if, the given object is a module
* dependence that is equal to this module dependence
*/
@Override
public boolean equals(Object ob) {
if (!(ob instanceof Requires))
return false;
Requires that = (Requires)ob;
return (name.equals(that.name) && mods.equals(that.mods));
}
/**
* Computes a hash code for this module dependence.
*
*
The hash code is based upon the module name and modifiers. It
* satisfies the general contract of the {@link Object#hashCode
* Object.hashCode} method.
*
* @return The hash-code value for this module dependence
*/
@Override
public int hashCode() {
return name.hashCode() * 43 + mods.hashCode();
}
/**
* Returns a string describing module dependence.
*
* @return A string describing module dependence
*/
@Override
public String toString() {
return ModuleDescriptor.toString(mods, name);
}
}
/**
*
A module export, may be qualified or unqualified.
*
* @see ModuleDescriptor#exports()
* @since 9
*/
public final static class Exports {
/**
* A modifier on a module export.
*
* @since 9
*/
public static enum Modifier {
/**
* The export was not explicitly or implicitly declared in the
* source of the module declaration.
*/
SYNTHETIC,
/**
* The export was implicitly declared in the source of the module
* declaration.
*/
MANDATED;
}
private final Set mods;
private final String source;
private final Set targets; // empty if unqualified export
/**
* Constructs an export
*/
private Exports(Set ms, String source, Set targets) {
if (ms.isEmpty()) {
ms = Collections.emptySet();
} else {
ms = Collections.unmodifiableSet(EnumSet.copyOf(ms));
}
this.mods = ms;
this.source = source;
this.targets = emptyOrUnmodifiableSet(targets);
}
private Exports(Set ms,
String source,
Set targets,
boolean unused) {
this.mods = ms;
this.source = source;
this.targets = targets;
}
/**
* Returns the set of modifiers.
*
* @return A possibly-empty unmodifiable set of modifiers
*/
public Set modifiers() {
return mods;
}
/**
* Returns {@code true} if this is a qualified export.
*
* @return {@code true} if this is a qualified export
*/
public boolean isQualified() {
return !targets.isEmpty();
}
/**
* Returns the package name.
*
* @return The package name
*/
public String source() {
return source;
}
/**
* For a qualified export, returns the non-empty and immutable set
* of the module names to which the package is exported. For an
* unqualified export, returns an empty set.
*
* @return The set of target module names or for an unqualified
* export, an empty set
*/
public Set targets() {
return targets;
}
/**
* Computes a hash code for this module export.
*
*
The hash code is based upon the modifiers, the package name,
* and for a qualified export, the set of modules names to which the
* package is exported. It satisfies the general contract of the
* {@link Object#hashCode Object.hashCode} method.
*
* @return The hash-code value for this module export
*/
@Override
public int hashCode() {
int hash = mods.hashCode();
hash = hash * 43 + source.hashCode();
return hash * 43 + targets.hashCode();
}
/**
* Tests this module export for equality with the given object.
*
*
If the given object is not an {@code Exports} then this method
* returns {@code false}. Two module exports objects are equal if their
* set of modifiers is equal, the package names are equal and the set
* of target module names is equal.
*
*
This method satisfies the general contract of the {@link
* java.lang.Object#equals(Object) Object.equals} method.
*
* @param ob
* the object to which this object is to be compared
*
* @return {@code true} if, and only if, the given object is a module
* dependence that is equal to this module dependence
*/
@Override
public boolean equals(Object ob) {
if (!(ob instanceof Exports))
return false;
Exports other = (Exports)ob;
return Objects.equals(this.mods, other.mods)
&& Objects.equals(this.source, other.source)
&& Objects.equals(this.targets, other.targets);
}
/**
* Returns a string describing module export.
*
* @return A string describing module export
*/
@Override
public String toString() {
String s = ModuleDescriptor.toString(mods, source);
if (targets.isEmpty())
return s;
else
return s + " to " + targets;
}
}
/**
*
Represents a module opens directive, 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
* members, not just public types and their public members to be reflected
* on by APIs that support private access or a way to bypass or suppress
* default Java language access control checks.
*
* @see ModuleDescriptor#opens()
* @since 9
*/
public final static class Opens {
/**
* A modifier on a module opens directive.
*
* @since 9
*/
public static enum Modifier {
/**
* The opens 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.
*/
MANDATED;
}
private final Set mods;
private final String source;
private final Set targets; // empty if unqualified export
/**
* Constructs an Opens
*/
private Opens(Set ms, String source, Set targets) {
if (ms.isEmpty()) {
ms = Collections.emptySet();
} else {
ms = Collections.unmodifiableSet(EnumSet.copyOf(ms));
}
this.mods = ms;
this.source = source;
this.targets = emptyOrUnmodifiableSet(targets);
}
private Opens(Set ms,
String source,
Set targets,
boolean unused) {
this.mods = ms;
this.source = source;
this.targets = targets;
}
/**
* Returns the set of modifiers.
*
* @return A possibly-empty unmodifiable set of modifiers
*/
public Set modifiers() {
return mods;
}
/**
* Returns {@code true} if this is a qualified opens.
*
* @return {@code true} if this is a qualified opens
*/
public boolean isQualified() {
return !targets.isEmpty();
}
/**
* Returns the package name.
*
* @return The package name
*/
public String source() {
return source;
}
/**
* For a qualified opens, returns the non-empty and immutable set
* of the module names to which the package is open. For an
* unqualified opens, returns an empty set.
*
* @return The set of target module names or for an unqualified
* opens, an empty set
*/
public Set targets() {
return targets;
}
/**
* Computes a hash code for this module opens.
*
*
The hash code is based upon the modifiers, the package name,
* and for a qualified opens, the set of modules names to which the
* package is opened. It satisfies the general contract of the
* {@link Object#hashCode Object.hashCode} method.
*
* @return The hash-code value for this module opens
*/
@Override
public int hashCode() {
int hash = mods.hashCode();
hash = hash * 43 + source.hashCode();
return hash * 43 + targets.hashCode();
}
/**
* Tests this module opens for equality with the given object.
*
*
If the given object is not an {@code Opens} then this method
* returns {@code false}. Two {@code Opens} objects are equal if their
* set of modifiers is equal, the package names are equal and the set
* of target module names is equal.
*
*
This method satisfies the general contract of the {@link
* java.lang.Object#equals(Object) Object.equals} method.
*
* @param ob
* the object to which this object is to be compared
*
* @return {@code true} if, and only if, the given object is a module
* dependence that is equal to this module dependence
*/
@Override
public boolean equals(Object ob) {
if (!(ob instanceof Opens))
return false;
Opens other = (Opens)ob;
return Objects.equals(this.mods, other.mods)
&& Objects.equals(this.source, other.source)
&& Objects.equals(this.targets, other.targets);
}
/**
* Returns a string describing module opens.
*
* @return A string describing module opens
*/
@Override
public String toString() {
String s = ModuleDescriptor.toString(mods, source);
if (targets.isEmpty())
return s;
else
return s + " to " + targets;
}
}
/**
*
A service that a module provides one or more implementations of.
*
* @see ModuleDescriptor#provides()
* @since 9
*/
public final static class Provides {
private final String service;
private final List providers;
private Provides(String service, List providers) {
this.service = service;
this.providers = Collections.unmodifiableList(providers);
}
private Provides(String service, List providers, boolean unused) {
this.service = service;
this.providers = providers;
}
/**
* Returns the fully qualified class name of the service type.
*
* @return The fully qualified class name of the service type.
*/
public String service() { return service; }
/**
* Returns the list of the fully qualified class names of the providers
* or provider factories.
*
* @return A non-empty and unmodifiable list of the fully qualified class
* names of the providers or provider factories
*/
public List providers() { return providers; }
/**
* Computes a hash code for this provides.
*
*
The hash code is based upon the service type and the set of
* providers. It satisfies the general contract of the {@link
* Object#hashCode Object.hashCode} method.
*
* @return The hash-code value for this module provides
*/
@Override
public int hashCode() {
return service.hashCode() * 43 + providers.hashCode();
}
/**
* Tests this provides for equality with the given object.
*
*
If the given object is not a {@code Provides} then this method
* returns {@code false}. Two {@code Provides} objects are equal if the
* service type is equal and the list of providers is equal.
*
*
This method satisfies the general contract of the {@link
* java.lang.Object#equals(Object) Object.equals} method.
*
* @param ob
* the object to which this object is to be compared
*
* @return {@code true} if, and only if, the given object is a
* {@code Provides} that is equal to this {@code Provides}
*/
@Override
public boolean equals(Object ob) {
if (!(ob instanceof Provides))
return false;
Provides other = (Provides)ob;
return Objects.equals(this.service, other.service) &&
Objects.equals(this.providers, other.providers);
}
/**
* Returns a string describing this provides.
*
* @return A string describing this provides
*/
@Override
public String toString() {
return service + " with " + providers;
}
}
/**
* A module's version string.
*
*
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
* 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
* nor punctuation characters, or vice versa.
*
*
*
*
The version number is a sequence of tokens separated by
* {@code '.'} characters, terminated by the first {@code '-'} or {@code
* '+'} character.
*
*
The pre-release version is a sequence of tokens separated
* by {@code '.'} or {@code '-'} characters, terminated by the first
* {@code '+'} character.
*
*
The build version is a sequence of tokens separated by
* {@code '.'}, {@code '-'}, or {@code '+'} characters.
*
*
*
*
When comparing two version strings, the elements of their
* corresponding components are compared in pointwise fashion. If one
* component is longer than the other, but otherwise equal to it, then the
* first component is considered the greater of the two; otherwise, if two
* corresponding elements are integers then they are compared as such;
* otherwise, at least one of the elements is a string, so the other is
* converted into a string if it is an integer and the two are compared
* lexicographically. Trailing integer elements with the value zero are
* ignored.
*
*
Given two version strings, if their version numbers differ then the
* result of comparing them is the result of comparing their version
* numbers; otherwise, if one of them has a pre-release version but the
* other does not then the first is considered to precede the second,
* otherwise the result of comparing them is the result of comparing their
* pre-release versions; otherwise, the result of comparing them is the
* result of comparing their build versions.
*
* @see ModuleDescriptor#version()
* @since 9
*/
public final static class Version
implements Comparable
{
private final String version;
// If Java had disjunctive types then we'd write List here
//
private final List