--- a/jdk/src/java.base/share/classes/java/lang/module/Configuration.java Wed Nov 23 16:16:35 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/Configuration.java Thu Dec 01 08:57:53 2016 +0000
@@ -26,14 +26,20 @@
package java.lang.module;
import java.io.PrintStream;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* The configuration that is the result of resolution or resolution with
@@ -46,14 +52,14 @@
* dependences expressed by {@code requires} clauses.
*
* The <em>dependence graph</em> is augmented with edges that take account of
- * implicitly declared dependences ({@code requires public}) to create a
+ * implicitly declared dependences ({@code requires transitive}) to create a
* <em>readability graph</em>. A {@code Configuration} encapsulates the
* resulting graph of {@link ResolvedModule resolved modules}.
*
* <p> Suppose we have the following observable modules: </p>
* <pre> {@code
* module m1 { requires m2; }
- * module m2 { requires public m3; }
+ * module m2 { requires transitive m3; }
* module m3 { }
* module m4 { }
* } </pre>
@@ -70,8 +76,10 @@
* <p> Resolution is an additive process. When computing the transitive closure
* then the dependence relation may include dependences on modules in parent
* configurations. The result is a <em>relative configuration</em> that is
- * relative to a parent configuration and where the readability graph may have
- * edges from modules in the configuration to modules in a parent configuration.
+ * relative to one or more parent configurations and where the readability graph
+ * may have edges from modules in the configuration to modules in parent
+ * configurations.
+ *
* </p>
*
* <p> Suppose we have the following observable modules: </p>
@@ -96,9 +104,9 @@
* <p> {@link ModuleDescriptor#isAutomatic() Automatic} modules receive special
* treatment during resolution. Each automatic module is resolved so that it
* reads all other modules in the configuration and all parent configurations.
- * Each automatic module is also resolved as if it {@code requires public} all
- * other automatic modules in the configuration (and all automatic modules in
- * parent configurations). </p>
+ * Each automatic module is also resolved as if it {@code requires transitive}
+ * all other automatic modules in the configuration (and all automatic modules
+ * in parent configurations). </p>
* <h2><a name="servicebinding">Service binding</a></h2>
*
@@ -171,54 +179,157 @@
// @see Configuration#empty()
private static final Configuration EMPTY_CONFIGURATION = new Configuration();
- private final Configuration parent;
+ // parent configurations, in search order
+ private final List<Configuration> parents;
private final Map<ResolvedModule, Set<ResolvedModule>> graph;
private final Set<ResolvedModule> modules;
private final Map<String, ResolvedModule> nameToModule;
private Configuration() {
- this.parent = null;
+ this.parents = Collections.emptyList();
this.graph = Collections.emptyMap();
this.modules = Collections.emptySet();
this.nameToModule = Collections.emptyMap();
}
- private Configuration(Configuration parent,
+ private Configuration(List<Configuration> parents,
Resolver resolver,
boolean check)
{
Map<ResolvedModule, Set<ResolvedModule>> g = resolver.finish(this, check);
- Map<String, ResolvedModule> nameToModule = new HashMap<>();
+ @SuppressWarnings(value = {"rawtypes", "unchecked"})
+ Entry<String, ResolvedModule>[] nameEntries
+ = (Entry<String, ResolvedModule>[])new Entry[g.size()];
+ ResolvedModule[] moduleArray = new ResolvedModule[g.size()];
+ int i = 0;
for (ResolvedModule resolvedModule : g.keySet()) {
- nameToModule.put(resolvedModule.name(), resolvedModule);
+ moduleArray[i] = resolvedModule;
+ nameEntries[i] = Map.entry(resolvedModule.name(), resolvedModule);
+ i++;
}
- this.parent = parent;
+ this.parents = Collections.unmodifiableList(parents);
this.graph = g;
- this.modules = Collections.unmodifiableSet(g.keySet());
- this.nameToModule = Collections.unmodifiableMap(nameToModule);
+ this.modules = Set.of(moduleArray);
+ this.nameToModule = Map.ofEntries(nameEntries);
}
/**
* Resolves a collection of root modules, with this configuration as its
- * parent, to create a new configuration.
+ * parent, to create a new configuration. This method works exactly as
+ * specified by the static {@link
+ * #resolveRequires(ModuleFinder,List,ModuleFinder,Collection) resolveRequires}
+ * method when invoked with this configuration as the parent. In other words,
+ * if this configuration is {@code cf} then this method is equivalent to
+ * invoking:
+ * <pre> {@code
+ * Configuration.resolveRequires(before, List.of(cf), after, roots);
+ * }</pre>
+ *
+ * @param before
+ * The <em>before</em> module finder to find modules
+ * @param after
+ * The <em>after</em> module finder to locate modules when a
+ * module cannot be located by the {@code before} module finder
+ * and the module is not in this configuration
+ * @param roots
+ * The possibly-empty collection of module names of the modules
+ * to resolve
+ *
+ * @return The configuration that is the result of resolving the given
+ * root modules
+ *
+ * @throws ResolutionException
+ * If resolution or the post-resolution checks fail
+ * @throws SecurityException
+ * If locating a module is denied by the security manager
+ */
+ public Configuration resolveRequires(ModuleFinder before,
+ ModuleFinder after,
+ Collection<String> roots)
+ {
+ return resolveRequires(before, List.of(this), after, roots);
+ }
+
+
+ /**
+ * Resolves a collection of root modules, with service binding, and with
+ * this configuration as its parent, to create a new configuration.
+ * This method works exactly as specified by the static {@link
+ * #resolveRequiresAndUses(ModuleFinder,List,ModuleFinder,Collection)
+ * resolveRequiresAndUses} method when invoked with this configuration
+ * as the parent. In other words, if this configuration is {@code cf} then
+ * this method is equivalent to invoking:
+ * <pre> {@code
+ * Configuration.resolveRequiresAndUses(before, List.of(cf), after, roots);
+ * }</pre>
+ *
+ *
+ * @param before
+ * The <em>before</em> module finder to find modules
+ * @param after
+ * The <em>after</em> module finder to locate modules when not
+ * located by the {@code before} module finder and this
+ * configuration
+ * @param roots
+ * The possibly-empty collection of module names of the modules
+ * to resolve
+ *
+ * @return The configuration that is the result of resolving the given
+ * root modules
+ *
+ * @throws ResolutionException
+ * If resolution or the post-resolution checks fail
+ * @throws SecurityException
+ * If locating a module is denied by the security manager
+ */
+ public Configuration resolveRequiresAndUses(ModuleFinder before,
+ ModuleFinder after,
+ Collection<String> roots)
+ {
+ return resolveRequiresAndUses(before, List.of(this), after, roots);
+ }
+
+
+ /**
+ * Resolves a collection of root modules, with service binding, and with
+ * the empty configuration as its parent. The post resolution checks
+ * are optionally run.
+ *
+ * This method is used to create the configuration for the boot layer.
+ */
+ static Configuration resolveRequiresAndUses(ModuleFinder finder,
+ Collection<String> roots,
+ boolean check,
+ PrintStream traceOutput)
+ {
+ List<Configuration> parents = List.of(empty());
+ Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput);
+ resolver.resolveRequires(roots).resolveUses();
+
+ return new Configuration(parents, resolver, check);
+ }
+
+
+ /**
+ * Resolves a collection of root modules to create a configuration.
*
* <p> Each root module is located using the given {@code before} module
* finder. If a module is not found then it is located in the parent
* configuration as if by invoking the {@link #findModule(String)
- * findModule} method. If not found then the module is located using the
- * given {@code after} module finder. The same search order is used to
- * locate transitive dependences. Root modules or dependences that are
- * located in a parent configuration are resolved no further and are not
- * included in the resulting configuration. </p>
+ * findModule} method on each parent in iteration order. If not found then
+ * the module is located using the given {@code after} module finder. The
+ * same search order is used to locate transitive dependences. Root modules
+ * or dependences that are located in a parent configuration are resolved
+ * no further and are not included in the resulting configuration. </p>
*
* <p> When all modules have been resolved then the resulting dependency
* graph is checked to ensure that it does not contain cycles. A
- * readability graph is constructed and then, in conjunction with the
- * module exports and service use, checked for consistency. </p>
+ * readability graph is constructed and in conjunction with the module
+ * exports and service use, checked for consistency. </p>
*
* <p> Resolution and the (post-resolution) consistency checks may fail for
* following reasons: </p>
@@ -262,6 +373,8 @@
*
* @param before
* The <em>before</em> module finder to find modules
+ * @param parents
+ * The list parent configurations in search order
* @param after
* The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder or in parent
@@ -274,31 +387,37 @@
* root modules
*
* @throws ResolutionException
- * If resolution or the post-resolution checks fail for any of the
- * reasons listed
+ * If resolution or the post-resolution checks fail
+ * @throws IllegalArgumentException
+ * If the list of parents is empty
* @throws SecurityException
* If locating a module is denied by the security manager
*/
- public Configuration resolveRequires(ModuleFinder before,
- ModuleFinder after,
- Collection<String> roots)
+ public static Configuration resolveRequires(ModuleFinder before,
+ List<Configuration> parents,
+ ModuleFinder after,
+ Collection<String> roots)
{
Objects.requireNonNull(before);
Objects.requireNonNull(after);
Objects.requireNonNull(roots);
- Resolver resolver = new Resolver(before, this, after, null);
+ List<Configuration> parentList = new ArrayList<>(parents);
+ if (parentList.isEmpty())
+ throw new IllegalArgumentException("'parents' is empty");
+
+ Resolver resolver = new Resolver(before, parentList, after, null);
resolver.resolveRequires(roots);
- return new Configuration(this, resolver, true);
+ return new Configuration(parentList, resolver, true);
}
-
/**
- * Resolves a collection of root modules, with service binding, and with
- * this configuration as its parent, to create a new configuration.
+ * Resolves a collection of root modules, with service binding, to create
+ * configuration.
*
- * <p> This method works exactly as specified by {@link #resolveRequires
+ * <p> This method works exactly as specified by {@link
+ * #resolveRequires(ModuleFinder,List,ModuleFinder,Collection)
* resolveRequires} except that the graph of resolved modules is augmented
* with modules induced by the service-use dependence relation. </p>
*
@@ -319,6 +438,8 @@
*
* @param before
* The <em>before</em> module finder to find modules
+ * @param parents
+ * The list parent configurations in search order
* @param after
* The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder or in parent
@@ -331,51 +452,35 @@
* root modules
*
* @throws ResolutionException
- * If resolution or the post-resolution checks fail for any of the
- * reasons listed
+ * If resolution or the post-resolution checks fail
+ * @throws IllegalArgumentException
+ * If the list of parents is empty
* @throws SecurityException
* If locating a module is denied by the security manager
*/
- public Configuration resolveRequiresAndUses(ModuleFinder before,
- ModuleFinder after,
- Collection<String> roots)
+ public static Configuration resolveRequiresAndUses(ModuleFinder before,
+ List<Configuration> parents,
+ ModuleFinder after,
+ Collection<String> roots)
{
Objects.requireNonNull(before);
Objects.requireNonNull(after);
Objects.requireNonNull(roots);
- Resolver resolver = new Resolver(before, this, after, null);
+ List<Configuration> parentList = new ArrayList<>(parents);
+ if (parentList.isEmpty())
+ throw new IllegalArgumentException("'parents' is empty");
+
+ Resolver resolver = new Resolver(before, parentList, after, null);
resolver.resolveRequires(roots).resolveUses();
- return new Configuration(this, resolver, true);
+ return new Configuration(parentList, resolver, true);
}
/**
- * Resolves a collection of root modules, with service binding, and with
- * the empty configuration as its parent. The post resolution checks
- * are optionally run.
- *
- * This method is used to create the configuration for the boot layer.
- */
- static Configuration resolveRequiresAndUses(ModuleFinder finder,
- Collection<String> roots,
- boolean check,
- PrintStream traceOutput)
- {
- Configuration parent = empty();
-
- Resolver resolver
- = new Resolver(finder, parent, ModuleFinder.of(), traceOutput);
- resolver.resolveRequires(roots).resolveUses();
-
- return new Configuration(parent, resolver, check);
- }
-
-
- /**
- * Returns the <em>empty</em> configuration. The empty configuration does
- * not contain any modules and does not have a parent.
+ * Returns the <em>empty</em> configuration. There are no modules in the
+ * empty configuration. It has no parents.
*
* @return The empty configuration
*/
@@ -385,13 +490,14 @@
/**
- * Returns this configuration's parent unless this is the {@linkplain #empty
- * empty configuration}, which has no parent.
+ * Returns an unmodifiable list of this configuration's parents, in search
+ * order. If this is the {@linkplain #empty empty configuration} then an
+ * empty list is returned.
*
- * @return This configuration's parent
+ * @return A possibly-empty unmodifiable list of this parent configurations
*/
- public Optional<Configuration> parent() {
- return Optional.ofNullable(parent);
+ public List<Configuration> parents() {
+ return parents;
}
@@ -408,23 +514,35 @@
/**
* Finds a resolved module in this configuration, or if not in this
- * configuration, the {@linkplain #parent parent} configurations.
+ * configuration, the {@linkplain #parents parent} configurations.
+ * Finding a module in parent configurations is equivalent to invoking
+ * {@code findModule} on each parent, in search order, until the module
+ * is found or all parents have been searched. In a <em>tree of
+ * configurations</em> then this is equivalent to a depth-first search.
*
* @param name
* The module name of the resolved module to find
*
* @return The resolved module with the given name or an empty {@code
* Optional} if there isn't a module with this name in this
- * configuration or any parent configuration
+ * configuration or any parent configurations
*/
public Optional<ResolvedModule> findModule(String name) {
Objects.requireNonNull(name);
- if (parent == null)
- return Optional.empty();
ResolvedModule m = nameToModule.get(name);
if (m != null)
return Optional.of(m);
- return parent().flatMap(x -> x.findModule(name));
+
+ if (!parents.isEmpty()) {
+ return configurations()
+ .skip(1) // skip this configuration
+ .map(cf -> cf.nameToModule)
+ .filter(map -> map.containsKey(name))
+ .map(map -> map.get(name))
+ .findFirst();
+ }
+
+ return Optional.empty();
}
@@ -444,9 +562,46 @@
}
/**
+ * Returns an ordered stream of configurations. The first element is this
+ * configuration, the remaining elements are the parent configurations
+ * in DFS order.
+ *
+ * @implNote For now, the assumption is that the number of elements will
+ * be very low and so this method does not use a specialized spliterator.
+ */
+ Stream<Configuration> configurations() {
+ List<Configuration> allConfigurations = this.allConfigurations;
+ if (allConfigurations == null) {
+ allConfigurations = new ArrayList<>();
+ Set<Configuration> visited = new HashSet<>();
+ Deque<Configuration> stack = new ArrayDeque<>();
+ visited.add(this);
+ stack.push(this);
+ while (!stack.isEmpty()) {
+ Configuration layer = stack.pop();
+ allConfigurations.add(layer);
+
+ // push in reverse order
+ for (int i = layer.parents.size() - 1; i >= 0; i--) {
+ Configuration parent = layer.parents.get(i);
+ if (!visited.contains(parent)) {
+ visited.add(parent);
+ stack.push(parent);
+ }
+ }
+ }
+ this.allConfigurations = Collections.unmodifiableList(allConfigurations);
+ }
+ return allConfigurations.stream();
+ }
+
+ private volatile List<Configuration> allConfigurations;
+
+
+ /**
* Returns a string describing this configuration.
*
- * @return A string describing this configuration
+ * @return A possibly empty string describing this configuration
*/
@Override
public String toString() {