8156680: jdeps implementation refresh
authormchung
Thu, 19 May 2016 10:55:33 -0700
changeset 38524 badd925c1d2f
parent 38523 e8ff97117086
child 38525 a2169f8fa712
8156680: jdeps implementation refresh Reviewed-by: dfuchs
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsWriter.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Module.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModulePaths.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Profile.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
langtools/test/tools/jdeps/APIDeps.java
langtools/test/tools/jdeps/Basic.java
langtools/test/tools/jdeps/CompilerUtils.java
langtools/test/tools/jdeps/DotFileTest.java
langtools/test/tools/jdeps/lib/CompilerUtils.java
langtools/test/tools/jdeps/lib/JdepsUtil.java
langtools/test/tools/jdeps/m/Foo.java
langtools/test/tools/jdeps/modules/CheckModuleTest.java
langtools/test/tools/jdeps/modules/GenModuleInfo.java
langtools/test/tools/jdeps/modules/ModuleMetaData.java
langtools/test/tools/jdeps/modules/ModuleTest.java
langtools/test/tools/jdeps/modules/SplitPackage.java
langtools/test/tools/jdeps/modules/TransitiveDeps.java
langtools/test/tools/jdeps/modules/patches/javax/annotation/NonNull.java
langtools/test/tools/jdeps/modules/src/m4/module-info.java
langtools/test/tools/jdeps/modules/src/m4/p4/internal/Impl.java
langtools/test/tools/jdeps/modules/src/m5/module-info.java
langtools/test/tools/jdeps/modules/src/m5/p5/Main.java
langtools/test/tools/jdeps/modules/src/m5/p5/internal/T.java
langtools/test/tools/jdeps/modules/src/m6/module-info.java
langtools/test/tools/jdeps/modules/src/m6/p6/indirect/UnsafeRef.java
langtools/test/tools/jdeps/modules/src/m6/p6/safe/Lib.java
langtools/test/tools/jdeps/modules/src/m7/module-info.java
langtools/test/tools/jdeps/modules/src/m7/p7/Main.java
langtools/test/tools/jdeps/modules/src/m8/module-info.java
langtools/test/tools/jdeps/modules/src/m8/p8/Main.java
langtools/test/tools/jdeps/modules/src/unsafe/module-info.java
langtools/test/tools/jdeps/modules/src/unsafe/org/indirect/UnsafeRef.java
langtools/test/tools/jdeps/modules/src/unsafe/org/safe/Lib.java
langtools/test/tools/jdeps/modules/src/unsafe/org/unsafe/UseUnsafe.java
langtools/test/tools/jdeps/unsupported/JDKUnsupportedTest.java
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java	Thu May 19 10:55:33 2016 -0700
@@ -25,23 +25,24 @@
 
 package com.sun.tools.jdeps;
 
-import java.io.PrintStream;
-import java.util.ArrayList;
+import static com.sun.tools.jdeps.JdepsConfiguration.*;
+
+import com.sun.tools.classfile.Dependency.Location;
+import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
+import java.util.MissingResourceException;
 import java.util.Objects;
+import java.util.Optional;
+import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import com.sun.tools.classfile.Dependency.Location;
-
 /**
  * Dependency Analyzer.
  */
@@ -52,6 +53,7 @@
      */
     public enum Type {
         SUMMARY,
+        MODULE,  // equivalent to summary in addition, print module descriptor
         PACKAGE,
         CLASS,
         VERBOSE
@@ -62,9 +64,11 @@
      * Only the accepted dependencies are recorded.
      */
     interface Filter {
-        boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
+        boolean accepts(Location origin, Archive originArchive,
+                        Location target, Archive targetArchive);
     }
 
+    protected final JdepsConfiguration configuration;
     protected final Type type;
     protected final Filter filter;
     protected final Map<Archive, Dependences> results = new HashMap<>();
@@ -78,7 +82,8 @@
      * @param type Type of the dependency analysis
      * @param filter
      */
-    public Analyzer(Type type, Filter filter) {
+    Analyzer(JdepsConfiguration config, Type type, Filter filter) {
+        this.configuration = config;
         this.type = type;
         this.filter = filter;
     }
@@ -86,16 +91,10 @@
     /**
      * Performs the dependency analysis on the given archives.
      */
-    public boolean run(Stream<? extends Archive> archives) {
-        return run(archives.collect(Collectors.toList()));
-    }
-
-    /**
-     * Performs the dependency analysis on the given archives.
-     */
-    public boolean run(Iterable<? extends Archive> archives) {
-        // build a map from Location to Archive
-        buildLocationArchiveMap(archives);
+    boolean run(Iterable<? extends Archive> archives,
+                Map<Location, Archive> locationMap)
+    {
+        this.locationToArchive.putAll(locationMap);
 
         // traverse and analyze all dependencies
         for (Archive archive : archives) {
@@ -106,40 +105,50 @@
         return true;
     }
 
-    protected void buildLocationArchiveMap(Iterable<? extends Archive> archives) {
-        // build a map from Location to Archive
-        for (Archive archive: archives) {
-            archive.getClasses()
-                   .forEach(l -> locationToArchive.putIfAbsent(l, archive));
-        }
+    /**
+     * Returns the analyzed archives
+     */
+    Set<Archive> archives() {
+        return results.keySet();
     }
 
-    public boolean hasDependences(Archive archive) {
+    /**
+     * Returns true if the given archive has dependences.
+     */
+    boolean hasDependences(Archive archive) {
         if (results.containsKey(archive)) {
             return results.get(archive).dependencies().size() > 0;
         }
         return false;
     }
 
-    public Set<String> dependences(Archive source) {
+    /**
+     * Returns the dependences, either class name or package name
+     * as specified in the given verbose level, from the given source.
+     */
+    Set<String> dependences(Archive source) {
         if (!results.containsKey(source)) {
             return Collections.emptySet();
         }
-        Dependences result = results.get(source);
-        return result.dependencies().stream()
-                     .map(Dep::target)
-                     .collect(Collectors.toSet());
+
+        return results.get(source).dependencies()
+                      .stream()
+                      .map(Dep::target)
+                      .collect(Collectors.toSet());
     }
 
-    public Stream<Archive> requires(Archive source) {
+    /**
+     * Returns the direct dependences of the given source
+     */
+    Stream<Archive> requires(Archive source) {
         if (!results.containsKey(source)) {
             return Stream.empty();
         }
-        Dependences result = results.get(source);
-        return result.requires().stream().filter(a -> !a.isEmpty());
+        return results.get(source).requires()
+                      .stream();
     }
 
-    public interface Visitor {
+    interface Visitor {
         /**
          * Visits a recorded dependency from origin to target which can be
          * a fully-qualified classname, a package name, a module or
@@ -153,7 +162,7 @@
      * Visit the dependencies of the given source.
      * If the requested level is SUMMARY, it will visit the required archives list.
      */
-    public void visitDependences(Archive source, Visitor v, Type level) {
+    void visitDependences(Archive source, Visitor v, Type level) {
         if (level == Type.SUMMARY) {
             final Dependences result = results.get(source);
             final Set<Archive> reqs = result.requires();
@@ -187,7 +196,7 @@
         }
     }
 
-    public void visitDependences(Archive source, Visitor v) {
+    void visitDependences(Archive source, Visitor v) {
         visitDependences(source, v, type);
     }
 
@@ -224,14 +233,28 @@
             }
         }
 
+        /*
+         * Returns the archive that contains the given location.
+         */
         Archive findArchive(Location t) {
+            // local in this archive
             if (archive.getClasses().contains(t))
                 return archive;
 
-            return locationToArchive.computeIfAbsent(t, _k -> NOT_FOUND);
+            Archive target;
+            if (locationToArchive.containsKey(t)) {
+                target = locationToArchive.get(t);
+            } else {
+                // special case JDK removed API
+                target = configuration.findClass(t)
+                    .orElseGet(() -> REMOVED_JDK_INTERNALS.contains(t)
+                                        ? REMOVED_JDK_INTERNALS
+                                        : NOT_FOUND);
+            }
+            return locationToArchive.computeIfAbsent(t, _k -> target);
         }
 
-        // return classname or package name depedning on the level
+        // return classname or package name depending on the level
         private String getLocationName(Location o) {
             if (level == Type.CLASS || level == Type.VERBOSE) {
                 return o.getClassName();
@@ -345,4 +368,66 @@
                     target, targetArchive.getName());
         }
     }
+
+    private static final JdkInternals REMOVED_JDK_INTERNALS = new JdkInternals();
+
+    private static class JdkInternals extends Module {
+        private final String BUNDLE = "com.sun.tools.jdeps.resources.jdkinternals";
+
+        private final Set<String> jdkinternals;
+        private final Set<String> jdkUnsupportedClasses;
+        private JdkInternals() {
+            super("JDK removed internal API");
+
+            try {
+                ResourceBundle rb = ResourceBundle.getBundle(BUNDLE);
+                this.jdkinternals = rb.keySet();
+            } catch (MissingResourceException e) {
+                throw new InternalError("Cannot find jdkinternals resource bundle");
+            }
+
+            this.jdkUnsupportedClasses = getUnsupportedClasses();
+        }
+
+        public boolean contains(Location location) {
+            if (jdkUnsupportedClasses.contains(location.getName() + ".class")) {
+                return false;
+            }
+
+            String cn = location.getClassName();
+            int i = cn.lastIndexOf('.');
+            String pn = i > 0 ? cn.substring(0, i) : "";
+            return jdkinternals.contains(cn) || jdkinternals.contains(pn);
+        }
+
+        @Override
+        public String name() {
+            return getName();
+        }
+
+        @Override
+        public boolean isExported(String pn) {
+            return false;
+        }
+
+        private Set<String> getUnsupportedClasses() {
+            // jdk.unsupported may not be observable
+            Optional<Module> om = Profile.FULL_JRE.findModule(JDK_UNSUPPORTED);
+            if (om.isPresent()) {
+                return om.get().reader().entries();
+            }
+
+            // find from local run-time image
+            SystemModuleFinder system = new SystemModuleFinder();
+            if (system.find(JDK_UNSUPPORTED).isPresent()) {
+                try {
+                    return system.getClassReader(JDK_UNSUPPORTED).entries();
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+
+            return Collections.emptySet();
+        }
+    }
 }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Archive.java	Thu May 19 10:55:33 2016 -0700
@@ -38,6 +38,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
 
 /**
  * Represents the source of the class files.
@@ -86,6 +87,10 @@
         return Module.UNNAMED_MODULE;
     }
 
+    public boolean contains(String entry) {
+        return reader.entries().contains(entry);
+    }
+
     public void addClass(Location origin) {
         deps.computeIfAbsent(origin, _k -> new HashSet<>());
     }
@@ -98,6 +103,15 @@
         return deps.keySet();
     }
 
+    public Stream<Location> getDependencies() {
+        return deps.values().stream()
+                   .flatMap(Set::stream);
+    }
+
+    public boolean hasDependences() {
+        return getDependencies().count() > 0;
+    }
+
     public void visitDependences(Visitor v) {
         for (Map.Entry<Location,Set<Location>> e: deps.entrySet()) {
             for (Location target : e.getValue()) {
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java	Thu May 19 10:55:33 2016 -0700
@@ -173,7 +173,7 @@
 
     static boolean isClass(Path file) {
         String fn = file.getFileName().toString();
-        return fn.endsWith(".class") && !fn.equals(MODULE_INFO);
+        return fn.endsWith(".class");
     }
 
     class FileIterator implements Iterator<ClassFile> {
@@ -306,7 +306,7 @@
         protected Set<String> scan() {
             try (JarFile jf = new JarFile(path.toFile())) {
                 return jf.stream().map(JarEntry::getName)
-                         .filter(n -> n.endsWith(".class") && !n.endsWith(MODULE_INFO))
+                         .filter(n -> n.endsWith(".class"))
                          .collect(Collectors.toSet());
             } catch (IOException e) {
                 throw new UncheckedIOException(e);
@@ -409,7 +409,7 @@
             while (entries.hasMoreElements()) {
                 JarEntry e = entries.nextElement();
                 String name = e.getName();
-                if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
+                if (name.endsWith(".class")) {
                     return e;
                 }
             }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java	Thu May 19 10:55:33 2016 -0700
@@ -24,360 +24,285 @@
  */
 package com.sun.tools.jdeps;
 
+import static com.sun.tools.jdeps.Module.*;
+import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
+import static java.util.stream.Collectors.*;
+
 import com.sun.tools.classfile.AccessFlags;
 import com.sun.tools.classfile.ClassFile;
 import com.sun.tools.classfile.ConstantPoolException;
 import com.sun.tools.classfile.Dependencies;
 import com.sun.tools.classfile.Dependency;
+import com.sun.tools.classfile.Dependency.Location;
 
 import java.io.IOException;
-import java.nio.file.Path;
-import java.util.ArrayList;
+import java.io.UncheckedIOException;
 import java.util.Deque;
 import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.HashSet;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.FutureTask;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import static com.sun.tools.jdeps.Module.*;
-import static com.sun.tools.jdeps.ModulePaths.SystemModulePath.JAVA_BASE;
+/**
+ * Parses class files and finds dependences
+ */
+class DependencyFinder {
+    private static Finder API_FINDER = new Finder(true);
+    private static Finder CLASS_FINDER = new Finder(false);
+
+    private final JdepsConfiguration configuration;
+    private final JdepsFilter filter;
 
-public class DependencyFinder {
-    private final List<Archive> roots = new ArrayList<>();
-    private final List<Archive> classpaths = new ArrayList<>();
-    private final List<Module> modulepaths = new ArrayList<>();
-    private final List<String> classes = new ArrayList<>();
-    private final boolean compileTimeView;
+    private final Map<Finder, Deque<Archive>> parsedArchives = new ConcurrentHashMap<>();
+    private final Map<Location, Archive> parsedClasses = new ConcurrentHashMap<>();
+
+    private final ExecutorService pool = Executors.newFixedThreadPool(2);
+    private final Deque<FutureTask<Set<Location>>> tasks = new ConcurrentLinkedDeque<>();
 
-    DependencyFinder(boolean compileTimeView) {
-        this.compileTimeView = compileTimeView;
+    DependencyFinder(JdepsConfiguration configuration,
+                     JdepsFilter filter) {
+        this.configuration = configuration;
+        this.filter = filter;
+        this.parsedArchives.put(API_FINDER, new ConcurrentLinkedDeque<>());
+        this.parsedArchives.put(CLASS_FINDER, new ConcurrentLinkedDeque<>());
     }
 
-    /*
-     * Adds a class name to the root set
-     */
-    void addClassName(String cn) {
-        classes.add(cn);
+    Map<Location, Archive> locationToArchive() {
+        return parsedClasses;
     }
 
-    /*
-     * Adds the archive of the given path to the root set
+    /**
+     * Returns the modules of all dependencies found
      */
-    void addRoot(Path path) {
-        addRoot(Archive.getInstance(path));
-    }
-
-    /*
-     * Adds the given archive to the root set
-     */
-    void addRoot(Archive archive) {
-        Objects.requireNonNull(archive);
-        if (!roots.contains(archive))
-            roots.add(archive);
+    Stream<Archive> getDependences(Archive source) {
+        return source.getDependencies()
+                     .map(this::locationToArchive)
+                     .filter(a -> a != source);
     }
 
     /**
-     * Add an archive specified in the classpath.
+     * Returns the location to archive map; or NOT_FOUND.
+     *
+     * Location represents a parsed class.
      */
-    void addClassPathArchive(Path path) {
-        addClassPathArchive(Archive.getInstance(path));
-    }
-
-    /**
-     * Add an archive specified in the classpath.
-     */
-    void addClassPathArchive(Archive archive) {
-        Objects.requireNonNull(archive);
-        classpaths.add(archive);
+    Archive locationToArchive(Location location) {
+        return parsedClasses.containsKey(location)
+            ? parsedClasses.get(location)
+            : configuration.findClass(location).orElse(NOT_FOUND);
     }
 
     /**
-     * Add an archive specified in the modulepath.
+     * Returns a map from an archive to its required archives
      */
-    void addModule(Module m) {
-        Objects.requireNonNull(m);
-        modulepaths.add(m);
+    Map<Archive, Set<Archive>> dependences() {
+        Map<Archive, Set<Archive>> map = new HashMap<>();
+        parsedArchives.values().stream()
+            .flatMap(Deque::stream)
+            .filter(a -> !a.isEmpty())
+            .forEach(source -> {
+                Set<Archive> deps = getDependences(source).collect(toSet());
+                if (!deps.isEmpty()) {
+                    map.put(source, deps);
+                }
+        });
+        return map;
+    }
+
+    boolean isParsed(Location location) {
+        return parsedClasses.containsKey(location);
     }
 
     /**
-     * Returns the root set.
+     * Parses all class files from the given archive stream and returns
+     * all target locations.
      */
-    List<Archive> roots() {
-        return roots;
+    public Set<Location> parse(Stream<? extends Archive> archiveStream) {
+        archiveStream.forEach(archive -> parse(archive, CLASS_FINDER));
+        return waitForTasksCompleted();
     }
 
     /**
-     * Returns a stream of all archives including the root set, module paths,
-     * and classpath.
-     *
-     * This only returns the archives with classes parsed.
+     * Parses the exported API class files from the given archive stream and
+     * returns all target locations.
      */
-    Stream<Archive> archives() {
-        Stream<Archive> archives = Stream.concat(roots.stream(), modulepaths.stream());
-        archives = Stream.concat(archives, classpaths.stream());
-        return archives.filter(a -> !a.isEmpty())
-                       .distinct();
+    public Set<Location> parseExportedAPIs(Stream<? extends Archive> archiveStream) {
+        archiveStream.forEach(archive -> parse(archive, API_FINDER));
+        return waitForTasksCompleted();
+    }
+
+    /**
+     * Parses the named class from the given archive and
+     * returns all target locations the named class references.
+     */
+    public Set<Location> parse(Archive archive, String name) {
+        try {
+            return parse(archive, CLASS_FINDER, name);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
     }
 
     /**
-     * Finds dependencies
-     *
-     * @param apiOnly  API only
-     * @param maxDepth depth of transitive dependency analysis; zero indicates
-     * @throws IOException
+     * Parses the exported API of the named class from the given archive and
+     * returns all target locations the named class references.
      */
-    void findDependencies(JdepsFilter filter, boolean apiOnly, int maxDepth)
-            throws IOException
+    public Set<Location> parseExportedAPIs(Archive archive, String name)
     {
-        Dependency.Finder finder =
-                apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
-                        : Dependencies.getClassDependencyFinder();
-
-        // list of archives to be analyzed
-        Set<Archive> roots = new LinkedHashSet<>(this.roots);
-
-        // include java.base in root set
-        roots.add(JAVA_BASE);
-
-        // If -include pattern specified, classes may be in module path or class path.
-        // To get compile time view analysis, all classes are analyzed.
-        // add all modules except JDK modules to root set
-        modulepaths.stream()
-                   .filter(filter::matches)
-                   .forEach(roots::add);
+        try {
+            return parse(archive, API_FINDER, name);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
 
-        // add classpath to the root set
-        classpaths.stream()
-                .filter(filter::matches)
-                .forEach(roots::add);
-
-        // transitive dependency
-        int depth = maxDepth > 0 ? maxDepth : Integer.MAX_VALUE;
+    private Optional<FutureTask<Set<Location>>> parse(Archive archive, Finder finder) {
+        if (parsedArchives.get(finder).contains(archive))
+            return Optional.empty();
 
-        // Work queue of names of classfiles to be searched.
-        // Entries will be unique, and for classes that do not yet have
-        // dependencies in the results map.
-        ConcurrentLinkedDeque<String> deque = new ConcurrentLinkedDeque<>();
-        ConcurrentSkipListSet<String> doneClasses = new ConcurrentSkipListSet<>();
-
-        TaskExecutor executor = new TaskExecutor(finder, filter, apiOnly, deque, doneClasses);
-        try {
-            // get the immediate dependencies of the input files
-            for (Archive source : roots) {
-                executor.task(source, deque);
-            }
-            executor.waitForTasksCompleted();
-
-            List<Archive> archives = Stream.concat(Stream.concat(roots.stream(),
-                    modulepaths.stream()),
-                    classpaths.stream())
-                    .collect(Collectors.toList());
+        parsedArchives.get(finder).add(archive);
 
-            // Additional pass to find archive where dependences are identified
-            // and also any specified classes, if any.
-            // If -R is specified, perform transitive dependency analysis.
-            Deque<String> unresolved = new LinkedList<>(classes);
-            do {
-                String name;
-                while ((name = unresolved.poll()) != null) {
-                    if (doneClasses.contains(name)) {
-                        continue;
+        trace("parsing %s %s%n", archive.getName(), archive.path());
+        FutureTask<Set<Location>> task = new FutureTask<>(new Callable<>() {
+            public Set<Location> call() throws Exception {
+                Set<Location> targets = new HashSet<>();
+                for (ClassFile cf : archive.reader().getClassFiles()) {
+                    String classFileName;
+                    try {
+                        classFileName = cf.getName();
+                    } catch (ConstantPoolException e) {
+                        throw new Dependencies.ClassFileError(e);
                     }
-                    if (compileTimeView) {
-                        final String cn = name + ".class";
-                        // parse all classes in the source archive
-                        Optional<Archive> source = archives.stream()
-                                .filter(a -> a.reader().entries().contains(cn))
-                                .findFirst();
-                        trace("%s compile time view %s%n", name, source.map(Archive::getName).orElse(" not found"));
-                        if (source.isPresent()) {
-                            executor.runTask(source.get(), deque);
-                        }
-                    }
-                    ClassFile cf = null;
-                    for (Archive archive : archives) {
-                        cf = archive.reader().getClassFile(name);
 
-                        if (cf != null) {
-                            String classFileName;
-                            try {
-                                classFileName = cf.getName();
-                            } catch (ConstantPoolException e) {
-                                throw new Dependencies.ClassFileError(e);
-                            }
-                            if (!doneClasses.contains(classFileName)) {
-                                // if name is a fully-qualified class name specified
-                                // from command-line, this class might already be parsed
-                                doneClasses.add(classFileName);
-                                for (Dependency d : finder.findDependencies(cf)) {
-                                    if (depth == 0) {
-                                        // ignore the dependency
-                                        archive.addClass(d.getOrigin());
-                                        break;
-                                    } else if (filter.accepts(d) && filter.accept(archive)) {
-                                        // continue analysis on non-JDK classes
-                                        archive.addClass(d.getOrigin(), d.getTarget());
-                                        String cn = d.getTarget().getName();
-                                        if (!doneClasses.contains(cn) && !deque.contains(cn)) {
-                                            deque.add(cn);
-                                        }
-                                    } else {
-                                        // ensure that the parsed class is added the archive
-                                        archive.addClass(d.getOrigin());
-                                    }
-                                }
-                            }
-                            break;
+                    // filter source class/archive
+                    String cn = classFileName.replace('/', '.');
+                    if (!finder.accept(archive, cn, cf.access_flags))
+                        continue;
+
+                    // tests if this class matches the -include
+                    if (!filter.matches(cn))
+                        continue;
+
+                    for (Dependency d : finder.findDependencies(cf)) {
+                        if (filter.accepts(d)) {
+                            archive.addClass(d.getOrigin(), d.getTarget());
+                            targets.add(d.getTarget());
+                        } else {
+                            // ensure that the parsed class is added the archive
+                            archive.addClass(d.getOrigin());
                         }
-                    }
-                    if (cf == null) {
-                        doneClasses.add(name);
+                        parsedClasses.putIfAbsent(d.getOrigin(), archive);
                     }
                 }
-                unresolved = deque;
-                deque = new ConcurrentLinkedDeque<>();
-            } while (!unresolved.isEmpty() && depth-- > 0);
-        } finally {
-            executor.shutdown();
-        }
-     }
+
+                return targets;
+            }
+        });
+        tasks.add(task);
+        pool.submit(task);
+        return Optional.of(task);
+    }
 
-    /**
-     * TaskExecutor creates FutureTask to analyze all classes in a given archive
-     */
-    private class TaskExecutor {
-        final ExecutorService pool;
-        final Dependency.Finder finder;
-        final JdepsFilter filter;
-        final boolean apiOnly;
-        final Set<String> doneClasses;
-        final Map<Archive, FutureTask<Void>> tasks = new HashMap<>();
+    private Set<Location> parse(Archive archive, Finder finder, String name)
+        throws IOException
+    {
+        ClassFile cf = archive.reader().getClassFile(name);
+        if (cf == null) {
+            throw new IllegalArgumentException(archive.getName() + " does not contain " + name);
+        }
 
-        TaskExecutor(Dependency.Finder finder,
-                     JdepsFilter filter,
-                     boolean apiOnly,
-                     ConcurrentLinkedDeque<String> deque,
-                     Set<String> doneClasses) {
-            this.pool = Executors.newFixedThreadPool(2);
-            this.finder = finder;
-            this.filter = filter;
-            this.apiOnly = apiOnly;
-            this.doneClasses = doneClasses;
+        Set<Location> targets = new HashSet<>();
+        String cn;
+        try {
+            cn =  cf.getName().replace('/', '.');
+        } catch (ConstantPoolException e) {
+            throw new Dependencies.ClassFileError(e);
         }
 
-        /**
-         * Creates a new task to analyze class files in the given archive.
-         * The dependences are added to the given deque for analysis.
-         */
-        FutureTask<Void> task(Archive archive, final ConcurrentLinkedDeque<String> deque) {
-            trace("parsing %s %s%n", archive.getName(), archive.path());
-            FutureTask<Void> task = new FutureTask<Void>(new Callable<Void>() {
-                public Void call() throws Exception {
-                    for (ClassFile cf : archive.reader().getClassFiles()) {
-                        String classFileName;
-                        try {
-                            classFileName = cf.getName();
-                        } catch (ConstantPoolException e) {
-                            throw new Dependencies.ClassFileError(e);
-                        }
+        if (!finder.accept(archive, cn, cf.access_flags))
+            return targets;
+
+        // tests if this class matches the -include
+        if (!filter.matches(cn))
+            return targets;
 
-                        // tests if this class matches the -include
-                        String cn = classFileName.replace('/', '.');
-                        if (!filter.matches(cn))
-                            continue;
+        // skip checking filter.matches
+        for (Dependency d : finder.findDependencies(cf)) {
+            if (filter.accepts(d)) {
+                targets.add(d.getTarget());
+                archive.addClass(d.getOrigin(), d.getTarget());
+            } else {
+                // ensure that the parsed class is added the archive
+                archive.addClass(d.getOrigin());
+            }
+            parsedClasses.putIfAbsent(d.getOrigin(), archive);
+        }
+        return targets;
+    }
 
-                        // if -apionly is specified, analyze only exported and public types
-                        if (apiOnly && !(isExported(archive, cn) && cf.access_flags.is(AccessFlags.ACC_PUBLIC)))
-                            continue;
-
-                        if (!doneClasses.contains(classFileName)) {
-                            doneClasses.add(classFileName);
-                        }
+    /*
+     * Waits until all submitted tasks are completed.
+     */
+    private Set<Location> waitForTasksCompleted() {
+        try {
+            Set<Location> targets = new HashSet<>();
+            FutureTask<Set<Location>> task;
+            while ((task = tasks.poll()) != null) {
+                // wait for completion
+                if (!task.isDone())
+                    targets.addAll(task.get());
+            }
+            return targets;
+        } catch (InterruptedException|ExecutionException e) {
+            throw new Error(e);
+        }
+    }
 
-                        for (Dependency d : finder.findDependencies(cf)) {
-                            if (filter.accepts(d) && filter.accept(archive)) {
-                                String name = d.getTarget().getName();
-                                if (!doneClasses.contains(name) && !deque.contains(name)) {
-                                    deque.add(name);
-                                }
-                                archive.addClass(d.getOrigin(), d.getTarget());
-                            } else {
-                                // ensure that the parsed class is added the archive
-                                archive.addClass(d.getOrigin());
-                            }
-                        }
-                    }
-                    return null;
-                }
-            });
-            tasks.put(archive, task);
-            pool.submit(task);
-            return task;
+    /*
+     * Shutdown the executor service.
+     */
+    void shutdown() {
+        pool.shutdown();
+    }
+
+    private interface SourceFilter {
+        boolean accept(Archive archive, String cn, AccessFlags accessFlags);
+    }
+
+    private static class Finder implements Dependency.Finder, SourceFilter {
+        private final Dependency.Finder finder;
+        private final boolean apiOnly;
+        Finder(boolean apiOnly) {
+            this.apiOnly = apiOnly;
+            this.finder = apiOnly
+                ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
+                : Dependencies.getClassDependencyFinder();
+
         }
 
-        /*
-         * This task will parse all class files of the given archive, if it's a new task.
-         * This method waits until the task is completed.
-         */
-        void runTask(Archive archive, final ConcurrentLinkedDeque<String> deque) {
-            if (tasks.containsKey(archive))
-                return;
+        @Override
+        public boolean accept(Archive archive, String cn, AccessFlags accessFlags) {
+            int i = cn.lastIndexOf('.');
+            String pn = i > 0 ? cn.substring(0, i) : "";
 
-            FutureTask<Void> task = task(archive, deque);
-            try {
-                // wait for completion
-                task.get();
-            } catch (InterruptedException|ExecutionException e) {
-                throw new Error(e);
-            }
+            // if -apionly is specified, analyze only exported and public types
+            // All packages are exported in unnamed module.
+            return apiOnly ? archive.getModule().isExported(pn) &&
+                                 accessFlags.is(AccessFlags.ACC_PUBLIC)
+                           : true;
         }
 
-        /*
-         * Waits until all submitted tasks are completed.
-         */
-        void waitForTasksCompleted() {
-            try {
-                for (FutureTask<Void> t : tasks.values()) {
-                    if (t.isDone())
-                        continue;
-
-                    // wait for completion
-                    t.get();
-                }
-            } catch (InterruptedException|ExecutionException e) {
-                throw new Error(e);
-            }
-        }
-
-        /*
-         * Shutdown the executor service.
-         */
-        void shutdown() {
-            pool.shutdown();
-        }
-
-        /**
-         * Tests if the given class name is exported by the given archive.
-         *
-         * All packages are exported in unnamed module.
-         */
-        private boolean isExported(Archive archive, String classname) {
-            int i = classname.lastIndexOf('.');
-            String pn = i > 0 ? classname.substring(0, i) : "";
-            return archive.getModule().isExported(pn);
+        @Override
+        public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
+            return finder.findDependencies(classfile);
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 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 com.sun.tools.jdeps;
+
+import com.sun.tools.classfile.Dependency.Location;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
+import static com.sun.tools.jdeps.Analyzer.Type.VERBOSE;
+import static com.sun.tools.jdeps.Module.trace;
+import static java.util.stream.Collectors.*;
+
+/**
+ * Dependency Analyzer.
+ *
+ * Type of filters:
+ * source filter: -include <pattern>
+ * target filter: -package, -regex, -requires
+ *
+ * The initial archive set for analysis includes
+ * 1. archives specified in the command line arguments
+ * 2. observable modules matching the source filter
+ * 3. classpath archives matching the source filter or target filter
+ * 4. -addmods and -m root modules
+ */
+public class DepsAnalyzer {
+    final JdepsConfiguration configuration;
+    final JdepsFilter filter;
+    final JdepsWriter writer;
+    final Analyzer.Type verbose;
+    final boolean apiOnly;
+
+    final DependencyFinder finder;
+    final Analyzer analyzer;
+    final List<Archive> rootArchives = new ArrayList<>();
+
+    // parsed archives
+    final Set<Archive> archives = new LinkedHashSet<>();
+
+    public DepsAnalyzer(JdepsConfiguration config,
+                        JdepsFilter filter,
+                        JdepsWriter writer,
+                        Analyzer.Type verbose,
+                        boolean apiOnly) {
+        this.configuration = config;
+        this.filter = filter;
+        this.writer = writer;
+        this.verbose = verbose;
+        this.apiOnly = apiOnly;
+
+        this.finder = new DependencyFinder(config, filter);
+        this.analyzer = new Analyzer(configuration, verbose, filter);
+
+        // determine initial archives to be analyzed
+        this.rootArchives.addAll(configuration.initialArchives());
+
+        // if -include pattern is specified, add the matching archives on
+        // classpath to the root archives
+        if (filter.hasIncludePattern() || filter.hasTargetFilter()) {
+            configuration.getModules().values().stream()
+                .filter(source -> filter.include(source) && filter.matches(source))
+                .forEach(this.rootArchives::add);
+        }
+
+        // class path archives
+        configuration.classPathArchives().stream()
+            .filter(filter::matches)
+            .forEach(this.rootArchives::add);
+
+        // Include the root modules for analysis
+        this.rootArchives.addAll(configuration.rootModules());
+
+        trace("analyze root archives: %s%n", this.rootArchives);
+    }
+
+    /*
+     * Perform runtime dependency analysis
+     */
+    public boolean run() throws IOException {
+        return run(false, 1);
+    }
+
+    /**
+     * Perform compile-time view or run-time view dependency analysis.
+     *
+     * @param compileTimeView
+     * @param maxDepth  depth of recursive analysis.  depth == 0 if -R is set
+     */
+    public boolean run(boolean compileTimeView, int maxDepth) throws IOException {
+        try {
+            // parse each packaged module or classpath archive
+            if (apiOnly) {
+                finder.parseExportedAPIs(rootArchives.stream());
+            } else {
+                finder.parse(rootArchives.stream());
+            }
+            archives.addAll(rootArchives);
+
+            int depth = maxDepth > 0 ? maxDepth : Integer.MAX_VALUE;
+
+            // transitive analysis
+            if (depth > 1) {
+                if (compileTimeView)
+                    transitiveArchiveDeps(depth-1);
+                else
+                    transitiveDeps(depth-1);
+            }
+
+            Set<Archive> archives = archives();
+
+            // analyze the dependencies collected
+            analyzer.run(archives, finder.locationToArchive());
+
+            writer.generateOutput(archives, analyzer);
+        } finally {
+            finder.shutdown();
+        }
+        return true;
+    }
+
+    /**
+     * Returns the archives for reporting that has matching dependences.
+     *
+     * If -requires is set, they should be excluded.
+     */
+    Set<Archive> archives() {
+        if (filter.requiresFilter().isEmpty()) {
+            return archives.stream()
+                .filter(filter::include)
+                .filter(Archive::hasDependences)
+                .collect(Collectors.toSet());
+        } else {
+            // use the archives that have dependences and not specified in -requires
+            return archives.stream()
+                .filter(filter::include)
+                .filter(source -> !filter.requiresFilter().contains(source))
+                .filter(source ->
+                        source.getDependencies()
+                              .map(finder::locationToArchive)
+                              .anyMatch(a -> a != source))
+                .collect(Collectors.toSet());
+        }
+    }
+
+    /**
+     * Returns the dependences, either class name or package name
+     * as specified in the given verbose level.
+     */
+    Stream<String> dependences() {
+        return analyzer.archives().stream()
+                       .map(analyzer::dependences)
+                       .flatMap(Set::stream)
+                       .distinct();
+    }
+
+    /**
+     * Returns the archives that contains the given locations and
+     * not parsed and analyzed.
+     */
+    private Set<Archive> unresolvedArchives(Stream<Location> locations) {
+        return locations.filter(l -> !finder.isParsed(l))
+                        .distinct()
+                        .map(configuration::findClass)
+                        .flatMap(Optional::stream)
+                        .filter(filter::include)
+                        .collect(toSet());
+    }
+
+    /*
+     * Recursively analyzes entire module/archives.
+    */
+    private void transitiveArchiveDeps(int depth) throws IOException {
+        Stream<Location> deps = archives.stream()
+                                        .flatMap(Archive::getDependencies);
+
+        // start with the unresolved archives
+        Set<Archive> unresolved = unresolvedArchives(deps);
+        do {
+            // parse all unresolved archives
+            Set<Location> targets = apiOnly
+                ? finder.parseExportedAPIs(unresolved.stream())
+                : finder.parse(unresolved.stream());
+            archives.addAll(unresolved);
+
+            // Add dependencies to the next batch for analysis
+            unresolved = unresolvedArchives(targets.stream());
+        } while (!unresolved.isEmpty() && depth-- > 0);
+    }
+
+    /*
+     * Recursively analyze the class dependences
+     */
+    private void transitiveDeps(int depth) throws IOException {
+        Stream<Location> deps = archives.stream()
+                                        .flatMap(Archive::getDependencies);
+
+        Deque<Location> unresolved = deps.collect(Collectors.toCollection(LinkedList::new));
+        ConcurrentLinkedDeque<Location> deque = new ConcurrentLinkedDeque<>();
+        do {
+            Location target;
+            while ((target = unresolved.poll()) != null) {
+                if (finder.isParsed(target))
+                    continue;
+
+                Archive archive = configuration.findClass(target).orElse(null);
+                if (archive != null && filter.include(archive)) {
+                    archives.add(archive);
+
+                    String name = target.getName();
+                    Set<Location> targets = apiOnly
+                            ? finder.parseExportedAPIs(archive, name)
+                            : finder.parse(archive, name);
+
+                    // build unresolved dependencies
+                    targets.stream()
+                           .filter(t -> !finder.isParsed(t))
+                           .forEach(deque::add);
+                }
+            }
+            unresolved = deque;
+            deque = new ConcurrentLinkedDeque<>();
+        } while (!unresolved.isEmpty() && depth-- > 0);
+    }
+
+    // ----- for testing purpose -----
+
+    public static enum Info {
+        REQUIRES,
+        REQUIRES_PUBLIC,
+        EXPORTED_API,
+        MODULE_PRIVATE,
+        QUALIFIED_EXPORTED_API,
+        INTERNAL_API,
+        JDK_INTERNAL_API
+    }
+
+    public static class Node {
+        public final String name;
+        public final String source;
+        public final Info info;
+        Node(String name, Info info) {
+            this(name, name, info);
+        }
+        Node(String name, String source, Info info) {
+            this.name = name;
+            this.source = source;
+            this.info = info;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            if (info != Info.REQUIRES && info != Info.REQUIRES_PUBLIC)
+                sb.append(source).append("/");
+
+            sb.append(name);
+            if (info == Info.QUALIFIED_EXPORTED_API)
+                sb.append(" (qualified)");
+            else if (info == Info.JDK_INTERNAL_API)
+                sb.append(" (JDK internal)");
+            else if (info == Info.INTERNAL_API)
+                sb.append(" (internal)");
+            return sb.toString();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Node))
+                return false;
+
+            Node other = (Node)o;
+            return this.name.equals(other.name) &&
+                    this.source.equals(other.source) &&
+                    this.info.equals(other.info);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = name.hashCode();
+            result = 31 * result + source.hashCode();
+            result = 31 * result + info.hashCode();
+            return result;
+        }
+    }
+
+    /**
+     * Returns a graph of module dependences.
+     *
+     * Each Node represents a module and each edge is a dependence.
+     * No analysis on "requires public".
+     */
+    public Graph<Node> moduleGraph() {
+        Graph.Builder<Node> builder = new Graph.Builder<>();
+
+        archives().stream()
+            .forEach(m -> {
+                Node u = new Node(m.getName(), Info.REQUIRES);
+                builder.addNode(u);
+                analyzer.requires(m)
+                    .map(req -> new Node(req.getName(), Info.REQUIRES))
+                    .forEach(v -> builder.addEdge(u, v));
+            });
+        return builder.build();
+    }
+
+    /**
+     * Returns a graph of dependences.
+     *
+     * Each Node represents a class or package per the specified verbose level.
+     * Each edge indicates
+     */
+    public Graph<Node> dependenceGraph() {
+        Graph.Builder<Node> builder = new Graph.Builder<>();
+
+        archives().stream()
+            .map(analyzer.results::get)
+            .filter(deps -> !deps.dependencies().isEmpty())
+            .flatMap(deps -> deps.dependencies().stream())
+            .forEach(d -> addEdge(builder, d));
+        return builder.build();
+    }
+
+    private void addEdge(Graph.Builder<Node> builder, Analyzer.Dep dep) {
+        Archive source = dep.originArchive();
+        Archive target = dep.targetArchive();
+        String pn = dep.target();
+        if ((verbose == CLASS || verbose == VERBOSE)) {
+            int i = dep.target().lastIndexOf('.');
+            pn = i > 0 ? dep.target().substring(0, i) : "";
+        }
+        final Info info;
+        if (source == target) {
+            info = Info.MODULE_PRIVATE;
+        } else if (!target.getModule().isNamed()) {
+            info = Info.EXPORTED_API;
+        } else if (target.getModule().isExported(pn)) {
+            info = Info.EXPORTED_API;
+        } else {
+            Module module = target.getModule();
+
+            if (!source.getModule().isJDK() && module.isJDK())
+                info = Info.JDK_INTERNAL_API;
+                // qualified exports or inaccessible
+            else if (module.isExported(pn, source.getModule().name()))
+                info = Info.QUALIFIED_EXPORTED_API;
+            else
+                info = Info.INTERNAL_API;
+        }
+
+        Node u = new Node(dep.origin(), source.getName(), info);
+        Node v = new Node(dep.target(), target.getName(), info);
+        builder.addEdge(u, v);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Graph.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 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 com.sun.tools.jdeps;
+
+import java.io.PrintWriter;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public final class Graph<T> {
+    private final Set<T> nodes;
+    private final Map<T, Set<T>> edges;
+
+    public Graph(Set<T> nodes, Map<T, Set<T>> edges) {
+        this.nodes = Collections.unmodifiableSet(nodes);
+        this.edges = Collections.unmodifiableMap(edges);
+    }
+
+    public Set<T> nodes() {
+        return nodes;
+    }
+
+    public Map<T, Set<T>> edges() {
+        return edges;
+    }
+
+    public Set<T> adjacentNodes(T u) {
+        return edges.get(u);
+    }
+
+    public boolean contains(T u) {
+        return nodes.contains(u);
+    }
+
+    public Set<Edge<T>> edgesFrom(T u) {
+        return edges.get(u).stream()
+                    .map(v -> new Edge<T>(u, v))
+                    .collect(Collectors.toSet());
+    }
+
+    /**
+     * Returns a new Graph after transitive reduction
+     */
+    public Graph<T> reduce() {
+        Builder<T> builder = new Builder<>();
+        nodes.stream()
+                .forEach(u -> {
+                    builder.addNode(u);
+                    edges.get(u).stream()
+                         .filter(v -> !pathExists(u, v, false))
+                         .forEach(v -> builder.addEdge(u, v));
+                });
+        return builder.build();
+    }
+
+    /**
+     * Returns a new Graph after transitive reduction.  All edges in
+     * the given g takes precedence over this graph.
+     *
+     * @throw IllegalArgumentException g must be a subgraph this graph
+     */
+    public Graph<T> reduce(Graph<T> g) {
+        boolean subgraph = nodes.containsAll(g.nodes) &&
+                g.edges.keySet().stream()
+                       .allMatch(u -> adjacentNodes(u).containsAll(g.adjacentNodes(u)));
+        if (!subgraph) {
+            throw new IllegalArgumentException(g + " is not a subgraph of " + this);
+        }
+
+        Builder<T> builder = new Builder<>();
+        nodes.stream()
+                .forEach(u -> {
+                    builder.addNode(u);
+                    // filter the edge if there exists a path from u to v in the given g
+                    // or there exists another path from u to v in this graph
+                    edges.get(u).stream()
+                         .filter(v -> !g.pathExists(u, v) && !pathExists(u, v, false))
+                         .forEach(v -> builder.addEdge(u, v));
+                });
+
+        // add the overlapped edges from this graph and the given g
+        g.edges().keySet().stream()
+                .forEach(u -> g.adjacentNodes(u).stream()
+                                .filter(v -> isAdjacent(u, v))
+                                .forEach(v -> builder.addEdge(u, v)));
+        return builder.build();
+    }
+
+    /**
+     * Returns nodes sorted in topological order.
+     */
+    public Stream<T> orderedNodes() {
+        TopoSorter<T> sorter = new TopoSorter<>(this);
+        return sorter.result.stream();
+    }
+
+    /**
+     * Traverse this graph and performs the given action in topological order
+     */
+    public void ordered(Consumer<T> action) {
+        TopoSorter<T> sorter = new TopoSorter<>(this);
+        sorter.ordered(action);
+    }
+
+    /**
+     * Traverses this graph and performs the given action in reverse topological order
+     */
+    public void reverse(Consumer<T> action) {
+        TopoSorter<T> sorter = new TopoSorter<>(this);
+        sorter.reverse(action);
+    }
+
+    /**
+     * Returns a transposed graph from this graph
+     */
+    public Graph<T> transpose() {
+        Builder<T> builder = new Builder<>();
+        builder.addNodes(nodes);
+        // reverse edges
+        edges.keySet().forEach(u -> {
+            edges.get(u).stream()
+                .forEach(v -> builder.addEdge(v, u));
+        });
+        return builder.build();
+    }
+
+    /**
+     * Returns all nodes reachable from the given set of roots.
+     */
+    public Set<T> dfs(Set<T> roots) {
+        Deque<T> deque = new LinkedList<>(roots);
+        Set<T> visited = new HashSet<>();
+        while (!deque.isEmpty()) {
+            T u = deque.pop();
+            if (!visited.contains(u)) {
+                visited.add(u);
+                if (contains(u)) {
+                    adjacentNodes(u).stream()
+                        .filter(v -> !visited.contains(v))
+                        .forEach(deque::push);
+                }
+            }
+        }
+        return visited;
+    }
+
+    private boolean isAdjacent(T u, T v) {
+        return edges.containsKey(u) && edges.get(u).contains(v);
+    }
+
+    private boolean pathExists(T u, T v) {
+        return pathExists(u, v, true);
+    }
+
+    /**
+     * Returns true if there exists a path from u to v in this graph.
+     * If includeAdjacent is false, it returns true if there exists
+     * another path from u to v of distance > 1
+     */
+    private boolean pathExists(T u, T v, boolean includeAdjacent) {
+        if (!nodes.contains(u) || !nodes.contains(v)) {
+            return false;
+        }
+        if (includeAdjacent && isAdjacent(u, v)) {
+            return true;
+        }
+        Deque<T> stack = new LinkedList<>();
+        Set<T> visited = new HashSet<>();
+        stack.push(u);
+        while (!stack.isEmpty()) {
+            T node = stack.pop();
+            if (node.equals(v)) {
+                return true;
+            }
+            if (!visited.contains(node)) {
+                visited.add(node);
+                edges.get(node).stream()
+                     .filter(e -> includeAdjacent || !node.equals(u) || !e.equals(v))
+                     .forEach(e -> stack.push(e));
+            }
+        }
+        assert !visited.contains(v);
+        return false;
+    }
+
+    public void printGraph(PrintWriter out) {
+        out.println("graph for " + nodes);
+        nodes.stream()
+             .forEach(u -> adjacentNodes(u).stream()
+                               .forEach(v -> out.format("  %s -> %s%n", u, v)));
+    }
+
+    @Override
+    public String toString() {
+        return nodes.toString();
+    }
+
+    static class Edge<T> {
+        final T u;
+        final T v;
+        Edge(T u, T v) {
+            this.u = u;
+            this.v = v;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s -> %s", u, v);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || !(o instanceof Edge))
+                return false;
+
+            @SuppressWarnings("unchecked")
+            Edge<T> edge = (Edge<T>) o;
+
+            return u.equals(edge.u) && v.equals(edge.v);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = u.hashCode();
+            result = 31 * result + v.hashCode();
+            return result;
+        }
+    }
+
+    static class Builder<T> {
+        final Set<T> nodes = new HashSet<>();
+        final Map<T, Set<T>> edges = new HashMap<>();
+
+        public void addNode(T node) {
+            if (nodes.contains(node)) {
+                return;
+            }
+            nodes.add(node);
+            edges.computeIfAbsent(node, _e -> new HashSet<>());
+        }
+
+        public void addNodes(Set<T> nodes) {
+            nodes.addAll(nodes);
+        }
+
+        public void addEdge(T u, T v) {
+            addNode(u);
+            addNode(v);
+            edges.get(u).add(v);
+        }
+
+        public Graph<T> build() {
+            return new Graph<T>(nodes, edges);
+        }
+    }
+
+    /**
+     * Topological sort
+     */
+    static class TopoSorter<T> {
+        final Deque<T> result = new LinkedList<>();
+        final Deque<T> nodes;
+        final Graph<T> graph;
+        TopoSorter(Graph<T> graph) {
+            this.graph = graph;
+            this.nodes = new LinkedList<>(graph.nodes);
+            sort();
+        }
+
+        public void ordered(Consumer<T> action) {
+            result.iterator().forEachRemaining(action);
+        }
+
+        public void reverse(Consumer<T> action) {
+            result.descendingIterator().forEachRemaining(action);
+        }
+
+        private void sort() {
+            Deque<T> visited = new LinkedList<>();
+            Deque<T> done = new LinkedList<>();
+            T node;
+            while ((node = nodes.poll()) != null) {
+                if (!visited.contains(node)) {
+                    visit(node, visited, done);
+                }
+            }
+        }
+
+        private void visit(T node, Deque<T> visited, Deque<T> done) {
+            if (visited.contains(node)) {
+                if (!done.contains(node)) {
+                    throw new IllegalArgumentException("Cyclic detected: " +
+                        node + " " + graph.edges().get(node));
+                }
+                return;
+            }
+            visited.add(node);
+            graph.edges().get(node).stream()
+                .forEach(x -> visit(x, visited, done));
+            done.add(node);
+            result.addLast(node);
+        }
+    }
+
+    public static class DotGraph {
+        static final String ORANGE = "#e76f00";
+        static final String BLUE = "#437291";
+        static final String GRAY = "#dddddd";
+
+        static final String REEXPORTS = "";
+        static final String REQUIRES = "style=\"dashed\"";
+        static final String REQUIRES_BASE = "color=\"" + GRAY + "\"";
+
+        static final Set<String> javaModules = modules(name ->
+            (name.startsWith("java.") && !name.equals("java.smartcardio")));
+        static final Set<String> jdkModules = modules(name ->
+            (name.startsWith("java.") ||
+                name.startsWith("jdk.") ||
+                name.startsWith("javafx.")) && !javaModules.contains(name));
+
+        private static Set<String> modules(Predicate<String> predicate) {
+            return ModuleFinder.ofSystem().findAll()
+                               .stream()
+                               .map(ModuleReference::descriptor)
+                               .map(ModuleDescriptor::name)
+                               .filter(predicate)
+                               .collect(Collectors.toSet());
+        }
+
+        static void printAttributes(PrintWriter out) {
+            out.format("  size=\"25,25\";%n");
+            out.format("  nodesep=.5;%n");
+            out.format("  ranksep=1.5;%n");
+            out.format("  pencolor=transparent;%n");
+            out.format("  node [shape=plaintext, fontname=\"DejaVuSans\", fontsize=36, margin=\".2,.2\"];%n");
+            out.format("  edge [penwidth=4, color=\"#999999\", arrowhead=open, arrowsize=2];%n");
+        }
+
+        static void printNodes(PrintWriter out, Graph<String> graph) {
+            out.format("  subgraph se {%n");
+            graph.nodes().stream()
+                 .filter(javaModules::contains)
+                 .forEach(mn -> out.format("  \"%s\" [fontcolor=\"%s\", group=%s];%n",
+                                           mn, ORANGE, "java"));
+            out.format("  }%n");
+            graph.nodes().stream()
+                 .filter(jdkModules::contains)
+                 .forEach(mn -> out.format("    \"%s\" [fontcolor=\"%s\", group=%s];%n",
+                                           mn, BLUE, "jdk"));
+
+            graph.nodes().stream()
+                 .filter(mn -> !javaModules.contains(mn) && !jdkModules.contains(mn))
+                 .forEach(mn -> out.format("  \"%s\";%n", mn));
+        }
+
+        static void printEdges(PrintWriter out, Graph<String> graph,
+                               String node, Set<String> requiresPublic) {
+            graph.adjacentNodes(node).forEach(dn -> {
+                String attr = dn.equals("java.base") ? REQUIRES_BASE
+                        : (requiresPublic.contains(dn) ? REEXPORTS : REQUIRES);
+                out.format("  \"%s\" -> \"%s\" [%s];%n", node, dn, attr);
+            });
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsConfiguration.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2012, 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 com.sun.tools.jdeps;
+
+import static com.sun.tools.jdeps.Module.trace;
+import static java.util.stream.Collectors.*;
+
+import com.sun.tools.classfile.Dependency;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.lang.module.ResolvedModule;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+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.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+public class JdepsConfiguration {
+    private static final String MODULE_INFO = "module-info.class";
+
+    private final SystemModuleFinder system;
+    private final ModuleFinder finder;
+
+    private final Map<String, Module> nameToModule = new LinkedHashMap<>();
+    private final Map<String, Module> packageToModule = new HashMap<>();
+    private final Map<String, List<Archive>> packageToUnnamedModule = new HashMap<>();
+
+    private final List<Archive> classpathArchives = new ArrayList<>();
+    private final List<Archive> initialArchives = new ArrayList<>();
+    private final Set<Module> rootModules = new HashSet<>();
+    private final Configuration configuration;
+
+    private JdepsConfiguration(SystemModuleFinder systemModulePath,
+                               ModuleFinder finder,
+                               Set<String> roots,
+                               List<Path> classpaths,
+                               List<Archive> initialArchives)
+        throws IOException
+    {
+        trace("root: %s%n", roots);
+
+        this.system = systemModulePath;
+        this.finder = finder;
+
+        // build root set
+        Set<String> mods = new HashSet<>();
+
+        if (initialArchives.isEmpty() && classpaths.isEmpty() && !roots.isEmpty()) {
+            // named module as root. No initial unnamed module
+            mods.addAll(roots);
+        } else {
+            // unnamed module
+            mods.addAll(roots);
+            mods.addAll(systemModulePath.defaultSystemRoots());
+        }
+
+        this.configuration = Configuration.empty()
+                .resolveRequires(finder, ModuleFinder.of(), mods);
+
+        this.configuration.modules().stream()
+                .map(ResolvedModule::reference)
+                .forEach(this::addModuleReference);
+
+        // packages in unnamed module
+        initialArchives.forEach(archive -> {
+            addPackagesInUnnamedModule(archive);
+            this.initialArchives.add(archive);
+        });
+
+        // classpath archives
+        for (Path p : classpaths) {
+            if (Files.exists(p)) {
+                Archive archive = Archive.getInstance(p);
+                addPackagesInUnnamedModule(archive);
+                classpathArchives.add(archive);
+            }
+        }
+
+        // all roots specified in -addmods or -m are included
+        // as the initial set for analysis.
+        roots.stream()
+             .map(nameToModule::get)
+             .forEach(this.rootModules::add);
+
+        initProfiles();
+
+        trace("resolved modules: %s%n", nameToModule.keySet().stream()
+                .sorted().collect(joining("\n", "\n", "")));
+    }
+
+    private void initProfiles() {
+        // other system modules are not observed and not added in nameToModule map
+        Map<String, Module> systemModules =
+            system.moduleNames()
+                .collect(toMap(Function.identity(), (mn) -> {
+                    Module m = nameToModule.get(mn);
+                    if (m == null) {
+                        ModuleReference mref = finder.find(mn).get();
+                        m = toModule(mref);
+                    }
+                    return m;
+                }));
+        Profile.init(systemModules);
+    }
+
+    private void addModuleReference(ModuleReference mref) {
+        Module module = toModule(mref);
+        nameToModule.put(mref.descriptor().name(), module);
+        mref.descriptor().packages()
+            .forEach(pn -> packageToModule.putIfAbsent(pn, module));
+    }
+
+    private void addPackagesInUnnamedModule(Archive archive) {
+        archive.reader().entries().stream()
+               .filter(e -> e.endsWith(".class") && !e.equals(MODULE_INFO))
+               .map(this::toPackageName)
+               .distinct()
+               .forEach(pn -> packageToUnnamedModule
+                   .computeIfAbsent(pn, _n -> new ArrayList<>()).add(archive));
+    }
+
+    private String toPackageName(String name) {
+        int i = name.lastIndexOf('/');
+        return i > 0 ? name.replace('/', '.').substring(0, i) : "";
+    }
+
+    public Optional<Module> findModule(String name) {
+        Objects.requireNonNull(name);
+        Module m = nameToModule.get(name);
+        return m!= null ? Optional.of(m) : Optional.empty();
+
+    }
+
+    public Optional<ModuleDescriptor> findModuleDescriptor(String name) {
+        Objects.requireNonNull(name);
+        Module m = nameToModule.get(name);
+        return m!= null ? Optional.of(m.descriptor()) : Optional.empty();
+    }
+
+    boolean isSystem(Module m) {
+        return system.find(m.name()).isPresent();
+    }
+
+    /**
+     * Returns the modules that the given module can read
+     */
+    public Stream<Module> reads(Module module) {
+        return configuration.findModule(module.name()).get()
+            .reads().stream()
+            .map(ResolvedModule::name)
+            .map(nameToModule::get);
+    }
+
+    /**
+     * Returns the list of packages that split between resolved module and
+     * unnamed module
+     */
+    public Map<String, Set<String>> splitPackages() {
+        Set<String> splitPkgs = packageToModule.keySet().stream()
+                                       .filter(packageToUnnamedModule::containsKey)
+                                       .collect(toSet());
+        if (splitPkgs.isEmpty())
+            return Collections.emptyMap();
+
+        return splitPkgs.stream().collect(toMap(Function.identity(), (pn) -> {
+            Set<String> sources = new LinkedHashSet<>();
+            sources.add(packageToModule.get(pn).getModule().location().toString());
+            packageToUnnamedModule.get(pn).stream()
+                .map(Archive::getPathName)
+                .forEach(sources::add);
+            return sources;
+        }));
+    }
+
+    /**
+     * Returns an optional archive containing the given Location
+     */
+    public Optional<Archive> findClass(Dependency.Location location) {
+        String name = location.getName();
+        int i = name.lastIndexOf('/');
+        String pn = i > 0 ? name.substring(0, i).replace('/', '.') : "";
+        Archive archive = packageToModule.get(pn);
+        if (archive != null) {
+            return archive.contains(name + ".class")
+                        ? Optional.of(archive)
+                        : Optional.empty();
+        }
+
+        if (packageToUnnamedModule.containsKey(pn)) {
+            return packageToUnnamedModule.get(pn).stream()
+                    .filter(a -> a.contains(name + ".class"))
+                    .findFirst();
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Returns the list of Modules that can be found in the specified
+     * module paths.
+     */
+    public Map<String, Module> getModules() {
+        return nameToModule;
+    }
+
+    public Stream<Module> resolve(Set<String> roots) {
+        if (roots.isEmpty()) {
+            return nameToModule.values().stream();
+        } else {
+            return Configuration.empty()
+                    .resolveRequires(finder, ModuleFinder.of(), roots)
+                    .modules().stream()
+                    .map(ResolvedModule::name)
+                    .map(nameToModule::get);
+        }
+    }
+
+    public List<Archive> classPathArchives() {
+        return classpathArchives;
+    }
+
+    public List<Archive> initialArchives() {
+        return initialArchives;
+    }
+
+    public Set<Module> rootModules() {
+        return rootModules;
+    }
+
+    public Module toModule(ModuleReference mref) {
+        try {
+            String mn = mref.descriptor().name();
+            URI location = mref.location().orElseThrow(FileNotFoundException::new);
+            ModuleDescriptor md = mref.descriptor();
+            Module.Builder builder = new Module.Builder(md, system.find(mn).isPresent());
+
+            final ClassFileReader reader;
+            if (location.getScheme().equals("jrt")) {
+                reader = system.getClassReader(mn);
+            } else {
+                reader = ClassFileReader.newInstance(Paths.get(location));
+            }
+
+            builder.classes(reader);
+            builder.location(location);
+
+            return builder.build();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    static class SystemModuleFinder implements ModuleFinder {
+        private static final String JAVA_SE = "java.se";
+
+        private final FileSystem fileSystem;
+        private final Path root;
+        private final Map<String, ModuleReference> systemModules;
+
+        SystemModuleFinder() {
+            this(System.getProperty("java.home"));
+        }
+        SystemModuleFinder(String javaHome) {
+            final FileSystem fs;
+            final Path root;
+            final Map<String, ModuleReference> systemModules;
+            if (javaHome != null) {
+                if (Files.isRegularFile(Paths.get(javaHome, "lib", "modules"))) {
+                    try {
+                        // jrt file system
+                        fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+                        root = fs.getPath("/modules");
+                        systemModules = Files.walk(root, 1)
+                            .filter(path -> !path.equals(root))
+                            .map(this::toModuleReference)
+                            .collect(toMap(mref -> mref.descriptor().name(),
+                                           Function.identity()));
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                } else {
+                    // exploded image
+                    fs = FileSystems.getDefault();
+                    root = Paths.get(javaHome, "modules");
+                    systemModules = ModuleFinder.ofSystem().findAll().stream()
+                        .collect(toMap(mref -> mref.descriptor().name(), Function.identity()));
+                }
+            } else {
+                fs = null;
+                root = null;
+                systemModules = Collections.emptyMap();
+            }
+            this.fileSystem = fs;
+            this.root = root;
+            this.systemModules = systemModules;
+        }
+
+        private ModuleReference toModuleReference(Path path) {
+            Path minfo = path.resolve(MODULE_INFO);
+            try (InputStream in = Files.newInputStream(minfo);
+                 BufferedInputStream bin = new BufferedInputStream(in)) {
+
+                ModuleDescriptor descriptor = dropHashes(ModuleDescriptor.read(bin));
+                String mn = descriptor.name();
+                URI uri = URI.create("jrt:/" + path.getFileName().toString());
+                Supplier<ModuleReader> readerSupplier = new Supplier<>() {
+                    @Override
+                    public ModuleReader get() {
+                        return new ModuleReader() {
+                            @Override
+                            public Optional<URI> find(String name) throws IOException {
+                                return name.equals(mn)
+                                    ? Optional.of(uri) : Optional.empty();
+                            }
+
+                            @Override
+                            public void close() throws IOException {
+                            }
+                        };
+                    }
+                };
+
+                return new ModuleReference(descriptor, uri, readerSupplier);
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+
+        private ModuleDescriptor dropHashes(ModuleDescriptor md) {
+            ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(md.name());
+            md.requires().forEach(builder::requires);
+            md.exports().forEach(builder::exports);
+            md.provides().values().stream().forEach(builder::provides);
+            md.uses().stream().forEach(builder::uses);
+            builder.conceals(md.conceals());
+            return builder.build();
+        }
+
+        @Override
+        public Set<ModuleReference> findAll() {
+            return systemModules.values().stream().collect(toSet());
+        }
+
+        @Override
+        public Optional<ModuleReference> find(String mn) {
+            return systemModules.containsKey(mn)
+                    ? Optional.of(systemModules.get(mn)) : Optional.empty();
+        }
+
+        public Stream<String> moduleNames() {
+            return systemModules.values().stream()
+                .map(mref -> mref.descriptor().name());
+        }
+
+        public ClassFileReader getClassReader(String modulename) throws IOException {
+            Path mp = root.resolve(modulename);
+            if (Files.exists(mp) && Files.isDirectory(mp)) {
+                return ClassFileReader.newInstance(fileSystem, mp);
+            } else {
+                throw new FileNotFoundException(mp.toString());
+            }
+        }
+
+        public Set<String> defaultSystemRoots() {
+            Set<String> roots = new HashSet<>();
+            boolean hasJava = false;
+            if (systemModules.containsKey(JAVA_SE)) {
+                // java.se is a system module
+                hasJava = true;
+                roots.add(JAVA_SE);
+            }
+
+            for (ModuleReference mref : systemModules.values()) {
+                String mn = mref.descriptor().name();
+                if (hasJava && mn.startsWith("java."))
+                    continue;
+
+                // add as root if observable and exports at least one package
+                ModuleDescriptor descriptor = mref.descriptor();
+                for (ModuleDescriptor.Exports e : descriptor.exports()) {
+                    if (!e.isQualified()) {
+                        roots.add(mn);
+                        break;
+                    }
+                }
+            }
+            return roots;
+        }
+    }
+
+    public static class Builder {
+        // the token for "all modules on the module path"
+        private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
+
+        final SystemModuleFinder systemModulePath;
+        final Set<String> rootModules = new HashSet<>();
+        final List<Archive> initialArchives = new ArrayList<>();
+        final List<Path> paths = new ArrayList<>();
+        final List<Path> classPaths = new ArrayList<>();
+
+        ModuleFinder upgradeModulePath;
+        ModuleFinder appModulePath;
+        boolean addAllApplicationModules;
+
+        public Builder() {
+            this(System.getProperty("java.home"));
+        }
+
+        public Builder(String systemModulePath) {
+            this.systemModulePath = new SystemModuleFinder(systemModulePath);;
+        }
+
+        public Builder upgradeModulePath(String upgradeModulePath) {
+            this.upgradeModulePath = createModulePathFinder(upgradeModulePath);
+            return this;
+        }
+
+        public Builder appModulePath(String modulePath) {
+            this.appModulePath = createModulePathFinder(modulePath);
+            return this;
+        }
+
+        public Builder addmods(Set<String> addmods) {
+            for (String mn : addmods) {
+                switch (mn) {
+                    case ALL_MODULE_PATH:
+                        this.addAllApplicationModules = true;
+                        break;
+                    default:
+                        this.rootModules.add(mn);
+                }
+            }
+            return this;
+        }
+
+        /*
+         * This method is for -check option to find all target modules specified
+         * in qualified exports.
+         *
+         * Include all system modules and modules found on modulepath
+         */
+        public Builder allModules() {
+            systemModulePath.moduleNames()
+                            .forEach(this.rootModules::add);
+            this.addAllApplicationModules = true;
+            return this;
+        }
+
+        public Builder addRoot(Path path) {
+            Archive archive = Archive.getInstance(path);
+            if (archive.contains(MODULE_INFO)) {
+                paths.add(path);
+            } else {
+                initialArchives.add(archive);
+            }
+            return this;
+        }
+
+        public Builder addClassPath(String classPath) {
+            this.classPaths.addAll(getClassPaths(classPath));
+            return this;
+        }
+
+        public JdepsConfiguration build() throws  IOException {
+            ModuleFinder finder = systemModulePath;
+            if (upgradeModulePath != null) {
+                finder = ModuleFinder.compose(upgradeModulePath, systemModulePath);
+            }
+            if (appModulePath != null) {
+                finder = ModuleFinder.compose(finder, appModulePath);
+            }
+            if (!paths.isEmpty()) {
+                ModuleFinder otherModulePath = ModuleFinder.of(paths.toArray(new Path[0]));
+
+                finder = ModuleFinder.compose(finder, otherModulePath);
+                // add modules specified on command-line (convenience) as root set
+                otherModulePath.findAll().stream()
+                        .map(mref -> mref.descriptor().name())
+                        .forEach(rootModules::add);
+            }
+            if (addAllApplicationModules && appModulePath != null) {
+                appModulePath.findAll().stream()
+                    .map(mref -> mref.descriptor().name())
+                    .forEach(rootModules::add);
+            }
+            return new JdepsConfiguration(systemModulePath,
+                                          finder,
+                                          rootModules,
+                                          classPaths,
+                                          initialArchives);
+        }
+
+        private static ModuleFinder createModulePathFinder(String mpaths) {
+            if (mpaths == null) {
+                return null;
+            } else {
+                String[] dirs = mpaths.split(File.pathSeparator);
+                Path[] paths = new Path[dirs.length];
+                int i = 0;
+                for (String dir : dirs) {
+                    paths[i++] = Paths.get(dir);
+                }
+                return ModuleFinder.of(paths);
+            }
+        }
+
+        /*
+         * Returns the list of Archive specified in cpaths and not included
+         * initialArchives
+         */
+        private List<Path> getClassPaths(String cpaths) {
+            if (cpaths.isEmpty()) {
+                return Collections.emptyList();
+            }
+            List<Path> paths = new ArrayList<>();
+            for (String p : cpaths.split(File.pathSeparator)) {
+                if (p.length() > 0) {
+                    // wildcard to parse all JAR files e.g. -classpath dir/*
+                    int i = p.lastIndexOf(".*");
+                    if (i > 0) {
+                        Path dir = Paths.get(p.substring(0, i));
+                        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
+                            for (Path entry : stream) {
+                                paths.add(entry);
+                            }
+                        } catch (IOException e) {
+                            throw new UncheckedIOException(e);
+                        }
+                    } else {
+                        paths.add(Paths.get(p));
+                    }
+                }
+            }
+            return paths;
+        }
+    }
+
+}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsFilter.java	Thu May 19 10:55:33 2016 -0700
@@ -41,22 +41,27 @@
  * 2. -filter:package to filter out same-package dependencies
  *    This filter is applied when jdeps parses the class files
  *    and filtered dependencies are not stored in the Analyzer.
- * 3. -module specifies to match target dependencies from the given module
+ * 3. -requires specifies to match target dependence from the given module
  *    This gets expanded into package lists to be filtered.
  * 4. -filter:archive to filter out same-archive dependencies
  *    This filter is applied later in the Analyzer as the
  *    containing archive of a target class may not be known until
  *    the entire archive
  */
-class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
+public class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
+
+    public static final JdepsFilter DEFAULT_FILTER =
+        new JdepsFilter.Builder().filter(true, true).build();
+
     private final Dependency.Filter filter;
     private final Pattern filterPattern;
     private final boolean filterSamePackage;
     private final boolean filterSameArchive;
     private final boolean findJDKInternals;
     private final Pattern includePattern;
-    private final Set<String> includePackages;
-    private final Set<String> excludeModules;
+    private final Pattern includeSystemModules;
+
+    private final Set<String> requires;
 
     private JdepsFilter(Dependency.Filter filter,
                         Pattern filterPattern,
@@ -64,16 +69,16 @@
                         boolean filterSameArchive,
                         boolean findJDKInternals,
                         Pattern includePattern,
-                        Set<String> includePackages,
-                        Set<String> excludeModules) {
+                        Pattern includeSystemModules,
+                        Set<String> requires) {
         this.filter = filter;
         this.filterPattern = filterPattern;
         this.filterSamePackage = filterSamePackage;
         this.filterSameArchive = filterSameArchive;
         this.findJDKInternals = findJDKInternals;
         this.includePattern = includePattern;
-        this.includePackages = includePackages;
-        this.excludeModules = excludeModules;
+        this.includeSystemModules = includeSystemModules;
+        this.requires = requires;
     }
 
     /**
@@ -82,12 +87,7 @@
      * @param cn fully-qualified name
      */
     public boolean matches(String cn) {
-        if (includePackages.isEmpty() && includePattern == null)
-            return true;
-
-        int i = cn.lastIndexOf('.');
-        String pn = i > 0 ? cn.substring(0, i) : "";
-        if (includePackages.contains(pn))
+        if (includePattern == null)
             return true;
 
         if (includePattern != null)
@@ -97,29 +97,39 @@
     }
 
     /**
-     * Tests if the given source includes classes specified in includePattern
-     * or includePackages filters.
+     * Tests if the given source includes classes specified in -include option
      *
      * This method can be used to determine if the given source should eagerly
      * be processed.
      */
     public boolean matches(Archive source) {
-        if (!includePackages.isEmpty() && source.getModule().isNamed()) {
-            boolean found = source.getModule().packages()
-                                  .stream()
-                                  .filter(pn -> includePackages.contains(pn))
-                                  .findAny().isPresent();
-            if (found)
-                return true;
+        if (includePattern != null) {
+            return source.reader().entries().stream()
+                    .map(name -> name.replace('/', '.'))
+                    .filter(name -> !name.equals("module-info.class"))
+                    .anyMatch(this::matches);
         }
-        if (!includePackages.isEmpty() || includePattern != null) {
-            return source.reader().entries()
-                         .stream()
-                         .map(name -> name.replace('/', '.'))
-                         .filter(this::matches)
-                         .findAny().isPresent();
-        }
-        return false;
+        return hasTargetFilter();
+    }
+
+    public boolean include(Archive source) {
+        Module module = source.getModule();
+        // skip system module by default; or if includeSystemModules is set
+        // only include the ones matching the pattern
+        return  !module.isSystem() || (includeSystemModules != null &&
+            includeSystemModules.matcher(module.name()).matches());
+    }
+
+    public boolean hasIncludePattern() {
+        return includePattern != null || includeSystemModules != null;
+    }
+
+    public boolean hasTargetFilter() {
+        return filter != null;
+    }
+
+    public Set<String> requiresFilter() {
+        return requires;
     }
 
     // ----- Dependency.Filter -----
@@ -164,42 +174,35 @@
         return true;
     }
 
-    /**
-     * Returns true if dependency should be recorded for the given source.
-     */
-    public boolean accept(Archive source) {
-        return !excludeModules.contains(source.getName());
-    }
-
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("exclude modules: ")
-          .append(excludeModules.stream().sorted().collect(Collectors.joining(",")))
-          .append("\n");
+        sb.append("include pattern: ").append(includePattern).append("\n");
         sb.append("filter same archive: ").append(filterSameArchive).append("\n");
         sb.append("filter same package: ").append(filterSamePackage).append("\n");
+        sb.append("requires: ").append(requires).append("\n");
         return sb.toString();
     }
 
-    static class Builder {
-        Dependency.Filter filter;
+    public static class Builder {
+        static Pattern SYSTEM_MODULE_PATTERN = Pattern.compile("java\\..*|jdk\\..*|javafx\\..*");
         Pattern filterPattern;
+        Pattern regex;
         boolean filterSamePackage;
         boolean filterSameArchive;
         boolean findJDKInterals;
         // source filters
         Pattern includePattern;
-        Set<String> includePackages = new HashSet<>();
-        Set<String> includeModules = new HashSet<>();
-        Set<String> excludeModules = new HashSet<>();
+        Pattern includeSystemModules;
+        Set<String> requires = new HashSet<>();
+        Set<String> targetPackages = new HashSet<>();
 
         public Builder packages(Set<String> packageNames) {
-            this.filter = Dependencies.getPackageFilter(packageNames, false);
+            this.targetPackages.addAll(packageNames);
             return this;
         }
         public Builder regex(Pattern regex) {
-            this.filter = Dependencies.getRegexFilter(regex);
+            this.regex = regex;
             return this;
         }
         public Builder filter(Pattern regex) {
@@ -211,6 +214,13 @@
             this.filterSameArchive = sameArchive;
             return this;
         }
+        public Builder requires(String name, Set<String> packageNames) {
+            this.requires.add(name);
+            this.targetPackages.addAll(packageNames);
+
+            includeIfSystemModule(name);
+            return this;
+        }
         public Builder findJDKInternals(boolean value) {
             this.findJDKInterals = value;
             return this;
@@ -219,30 +229,33 @@
             this.includePattern = regex;
             return this;
         }
-        public Builder includePackage(String pn) {
-            this.includePackages.add(pn);
+        public Builder includeSystemModules(Pattern regex) {
+            this.includeSystemModules = regex;
             return this;
         }
-        public Builder includeModules(Set<String> includes) {
-            this.includeModules.addAll(includes);
-            return this;
-        }
-        public Builder excludeModules(Set<String> excludes) {
-            this.excludeModules.addAll(excludes);
+        public Builder includeIfSystemModule(String name) {
+            if (includeSystemModules == null &&
+                    SYSTEM_MODULE_PATTERN.matcher(name).matches()) {
+                this.includeSystemModules = SYSTEM_MODULE_PATTERN;
+            }
             return this;
         }
 
-        JdepsFilter build() {
+        public JdepsFilter build() {
+            Dependency.Filter filter = null;
+            if (regex != null)
+                filter = Dependencies.getRegexFilter(regex);
+            else if (!targetPackages.isEmpty()) {
+                filter = Dependencies.getPackageFilter(targetPackages, false);
+            }
             return new JdepsFilter(filter,
                                    filterPattern,
                                    filterSamePackage,
                                    filterSameArchive,
                                    findJDKInterals,
                                    includePattern,
-                                   includePackages,
-                                   excludeModules.stream()
-                                        .filter(mn -> !includeModules.contains(mn))
-                                        .collect(Collectors.toSet()));
+                                   includeSystemModules,
+                                   requires);
         }
 
     }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Thu May 19 10:55:33 2016 -0700
@@ -25,31 +25,26 @@
 
 package com.sun.tools.jdeps;
 
+import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
 import static com.sun.tools.jdeps.Analyzer.Type.*;
 import static com.sun.tools.jdeps.JdepsWriter.*;
-import static com.sun.tools.jdeps.ModuleAnalyzer.Graph;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.module.ResolutionException;
-import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
-import java.util.Optional;
 import java.util.ResourceBundle;
 import java.util.Set;
-import java.util.TreeMap;
+import java.util.function.Function;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -58,7 +53,12 @@
  * Implementation for the jdeps tool for static class dependency analysis.
  */
 class JdepsTask {
-    static class BadArgs extends Exception {
+    static interface BadArguments {
+        String getKey();
+        Object[] getArgs();
+        boolean showUsage();
+    }
+    static class BadArgs extends Exception implements BadArguments {
         static final long serialVersionUID = 8765093759964640721L;
         BadArgs(String key, Object... args) {
             super(JdepsTask.getMessage(key, args));
@@ -73,6 +73,44 @@
         final String key;
         final Object[] args;
         boolean showUsage;
+
+        @Override
+        public String getKey() {
+            return key;
+        }
+
+        @Override
+        public Object[] getArgs() {
+            return args;
+        }
+
+        @Override
+        public boolean showUsage() {
+            return showUsage;
+        }
+    }
+
+    static class UncheckedBadArgs extends RuntimeException implements BadArguments {
+        static final long serialVersionUID = -1L;
+        final BadArgs cause;
+        UncheckedBadArgs(BadArgs cause) {
+            super(cause);
+            this.cause = cause;
+        }
+        @Override
+        public String getKey() {
+            return cause.key;
+        }
+
+        @Override
+        public Object[] getArgs() {
+            return cause.args;
+        }
+
+        @Override
+        public boolean showUsage() {
+            return cause.showUsage;
+        }
     }
 
     static abstract class Option {
@@ -126,7 +164,7 @@
                 if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
                     throw new BadArgs("err.invalid.path", arg);
                 }
-                task.options.dotOutputDir = arg;
+                task.options.dotOutputDir = Paths.get(arg);;
             }
         },
         new Option(false, "-s", "-summary") {
@@ -136,8 +174,9 @@
             }
         },
         new Option(false, "-v", "-verbose",
-                          "-verbose:package",
-                          "-verbose:class") {
+                                "-verbose:module",
+                                "-verbose:package",
+                                "-verbose:class") {
             void process(JdepsTask task, String opt, String arg) throws BadArgs {
                 switch (opt) {
                     case "-v":
@@ -146,6 +185,9 @@
                         task.options.filterSameArchive = false;
                         task.options.filterSamePackage = false;
                         break;
+                    case "-verbose:module":
+                        task.options.verbose = MODULE;
+                        break;
                     case "-verbose:package":
                         task.options.verbose = PACKAGE;
                         break;
@@ -157,6 +199,61 @@
                 }
             }
         },
+        new Option(false, "-apionly") {
+            void process(JdepsTask task, String opt, String arg) {
+                task.options.apiOnly = true;
+            }
+        },
+        new Option(true, "-check") {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                Set<String> mods =  Set.of(arg.split(","));
+                task.options.checkModuleDeps = mods;
+                task.options.addmods.addAll(mods);
+            }
+        },
+        new Option(true, "-genmoduleinfo") {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                Path p = Paths.get(arg);
+                if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
+                    throw new BadArgs("err.invalid.path", arg);
+                }
+                task.options.genModuleInfo = Paths.get(arg);
+            }
+        },
+        new Option(false, "-jdkinternals") {
+            void process(JdepsTask task, String opt, String arg) {
+                task.options.findJDKInternals = true;
+                task.options.verbose = CLASS;
+                if (task.options.includePattern == null) {
+                    task.options.includePattern = Pattern.compile(".*");
+                }
+            }
+        },
+
+        // ---- paths option ----
+        new Option(true, "-cp", "-classpath") {
+            void process(JdepsTask task, String opt, String arg) {
+                task.options.classpath = arg;
+            }
+        },
+        new Option(true, "-mp", "-modulepath") {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                task.options.modulePath = arg;
+            }
+        },
+        new Option(true, "-upgrademodulepath") {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                task.options.upgradeModulePath = arg;
+            }
+        },
+        new Option(true, "-m") {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                task.options.rootModule = arg;
+                task.options.addmods.add(arg);
+            }
+        },
+
+        // ---- Target filtering options ----
         new Option(true, "-p", "-package") {
             void process(JdepsTask task, String opt, String arg) {
                 task.options.packageNames.add(arg);
@@ -198,26 +295,24 @@
                 }
             }
         },
+
+        // ---- Source filtering options ----
         new Option(true, "-include") {
             void process(JdepsTask task, String opt, String arg) throws BadArgs {
                 task.options.includePattern = Pattern.compile(arg);
             }
         },
+
+        // Another alternative to list modules in -addmods option
+        new HiddenOption(true, "-include-system-modules") {
+            void process(JdepsTask task, String opt, String arg) throws BadArgs {
+                task.options.includeSystemModulePattern = Pattern.compile(arg);
+            }
+        },
+
         new Option(false, "-P", "-profile") {
             void process(JdepsTask task, String opt, String arg) throws BadArgs {
                 task.options.showProfile = true;
-                task.options.showModule = false;
-            }
-        },
-        new Option(false, "-M") {
-            void process(JdepsTask task, String opt, String arg) throws BadArgs {
-                task.options.showModule = true;
-                task.options.showProfile = false;
-            }
-        },
-        new Option(false, "-apionly") {
-            void process(JdepsTask task, String opt, String arg) {
-                task.options.apiOnly = true;
             }
         },
         new Option(false, "-R", "-recursive") {
@@ -228,15 +323,6 @@
                 task.options.filterSamePackage = false;
             }
         },
-        new Option(true, "-genmoduleinfo") {
-            void process(JdepsTask task, String opt, String arg) throws BadArgs {
-                Path p = Paths.get(arg);
-                if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
-                    throw new BadArgs("err.invalid.path", arg);
-                }
-                task.options.genModuleInfo = arg;
-            }
-        },
         new Option(false, "-ct", "-compile-time") {
             void process(JdepsTask task, String opt, String arg) {
                 task.options.compileTimeView = true;
@@ -245,62 +331,11 @@
                 task.options.depth = 0;
             }
         },
-        new Option(false, "-jdkinternals") {
-            void process(JdepsTask task, String opt, String arg) {
-                task.options.findJDKInternals = true;
-                task.options.verbose = CLASS;
-                if (task.options.includePattern == null) {
-                    task.options.includePattern = Pattern.compile(".*");
-                }
-            }
-        },
-        new Option(true, "-cp", "-classpath") {
-            void process(JdepsTask task, String opt, String arg) {
-                    task.options.classpath = arg;
-                }
-        },
-        new Option(true, "-mp", "-modulepath") {
-            void process(JdepsTask task, String opt, String arg) throws BadArgs {
-                task.options.modulePath = arg;
-                task.options.showModule = true;
-            }
-        },
-        new Option(true, "-upgrademodulepath") {
-            void process(JdepsTask task, String opt, String arg) throws BadArgs {
-                task.options.upgradeModulePath = arg;
-                task.options.showModule = true;
-            }
-        },
-        new Option(true, "-m") {
-            void process(JdepsTask task, String opt, String arg) throws BadArgs {
-                task.options.rootModule = arg;
-                task.options.includes.add(arg);
-                task.options.showModule = true;
-            }
-        },
-        new Option(false, "-check") {
-            void process(JdepsTask task, String opt, String arg) throws BadArgs {
-                task.options.checkModuleDeps = true;
-            }
-        },
-        new HiddenOption(true, "-include-modules") {
-            void process(JdepsTask task, String opt, String arg) throws BadArgs {
-                Arrays.stream(arg.split(","))
-                        .forEach(task.options.includes::add);
-                task.options.showModule = true;
-            }
-        },
-        new HiddenOption(true, "-exclude-modules") {
-                void process(JdepsTask task, String opt, String arg) throws BadArgs {
-                    Arrays.stream(arg.split(","))
-                            .forEach(task.options.excludes::add);
-                    task.options.showModule = true;
-                }
-        },
+
         new Option(false, "-q", "-quiet") {
             void process(JdepsTask task, String opt, String arg) {
-                    task.options.nowarning = true;
-                }
+                task.options.nowarning = true;
+            }
         },
 
         new Option(false, "-version") {
@@ -318,7 +353,11 @@
                 task.options.showLabel = true;
             }
         },
-
+        new HiddenOption(false, "-hide-module") {
+            void process(JdepsTask task, String opt, String arg) {
+                task.options.showModule = false;
+            }
+        },
         new HiddenOption(true, "-depth") {
             void process(JdepsTask task, String opt, String arg) throws BadArgs {
                 try {
@@ -332,7 +371,7 @@
 
     private static final String PROGNAME = "jdeps";
     private final Options options = new Options();
-    private final List<String> classes = new ArrayList<>();
+    private final List<String> inputArgs = new ArrayList<>();
 
     private PrintWriter log;
     void setLog(PrintWriter out) {
@@ -342,13 +381,13 @@
     /**
      * Result codes.
      */
-    static final int EXIT_OK = 0, // Completed with no errors.
-                     EXIT_ERROR = 1, // Completed but reported errors.
-                     EXIT_CMDERR = 2, // Bad command-line arguments
-                     EXIT_SYSERR = 3, // System error or resource exhaustion.
-                     EXIT_ABNORMAL = 4;// terminated abnormally
+    static final int EXIT_OK = 0,       // Completed with no errors.
+                     EXIT_ERROR = 1,    // Completed but reported errors.
+                     EXIT_CMDERR = 2,   // Bad command-line arguments
+                     EXIT_SYSERR = 3,   // System error or resource exhaustion.
+                     EXIT_ABNORMAL = 4; // terminated abnormally
 
-    int run(String[] args) {
+    int run(String... args) {
         if (log == null) {
             log = new PrintWriter(System.out);
         }
@@ -360,15 +399,11 @@
             if (options.version || options.fullVersion) {
                 showVersion(options.fullVersion);
             }
-            if (options.rootModule != null && !classes.isEmpty()) {
-                reportError("err.invalid.module.option", options.rootModule, classes);
-                return EXIT_CMDERR;
+            if (!inputArgs.isEmpty() && options.rootModule != null) {
+                reportError("err.invalid.arg.for.option", "-m");
             }
-            if (options.checkModuleDeps && options.rootModule == null) {
-                reportError("err.root.module.not.set");
-                return EXIT_CMDERR;
-            }
-            if (classes.isEmpty() && options.rootModule == null && options.includePattern == null) {
+            if (inputArgs.isEmpty() && options.addmods.isEmpty() && options.includePattern == null
+                    && options.includeSystemModulePattern == null && options.checkModuleDeps == null) {
                 if (options.help || options.version || options.fullVersion) {
                     return EXIT_OK;
                 } else {
@@ -377,19 +412,10 @@
                 }
             }
             if (options.genModuleInfo != null) {
-                if (options.dotOutputDir != null || !options.classpath.isEmpty() || options.hasFilter()) {
+                if (options.dotOutputDir != null || options.classpath != null || options.hasFilter()) {
                     showHelp();
                     return EXIT_CMDERR;
                 }
-                // default to compile time view analysis
-                options.compileTimeView = true;
-                for (String fn : classes) {
-                    Path p = Paths.get(fn);
-                    if (!Files.exists(p) || !fn.endsWith(".jar")) {
-                        reportError("err.genmoduleinfo.not.jarfile", fn);
-                        return EXIT_CMDERR;
-                    }
-                }
             }
 
             if (options.numFilters() > 1) {
@@ -405,12 +431,15 @@
                 showHelp();
                 return EXIT_CMDERR;
             }
+            if (options.checkModuleDeps != null && !inputArgs.isEmpty()) {
+                reportError("err.invalid.module.option", inputArgs, "-check");
+            }
 
             boolean ok = run();
             return ok ? EXIT_OK : EXIT_ERROR;
-        } catch (BadArgs e) {
-            reportError(e.key, e.args);
-            if (e.showUsage) {
+        } catch (BadArgs|UncheckedBadArgs e) {
+            reportError(e.getKey(), e.getArgs());
+            if (e.showUsage()) {
                 log.println(getMessage("main.usage.summary", PROGNAME));
             }
             return EXIT_CMDERR;
@@ -419,176 +448,178 @@
             return EXIT_CMDERR;
         } catch (IOException e) {
             e.printStackTrace();
-            return EXIT_ABNORMAL;
+            return EXIT_CMDERR;
         } finally {
             log.flush();
         }
     }
 
-    private ModulePaths modulePaths;
-    private boolean run() throws BadArgs, IOException {
-        DependencyFinder dependencyFinder =
-            new DependencyFinder(options.compileTimeView);
+    boolean run() throws IOException {
+        JdepsConfiguration config = buildConfig();
 
-        buildArchive(dependencyFinder);
+        // detect split packages
+        config.splitPackages().entrySet().stream()
+            .sorted(Map.Entry.comparingByKey())
+            .forEach(e -> System.out.format("split package: %s %s%n", e.getKey(),
+                                            e.getValue().toString()));
 
-        if (options.rootModule != null &&
-                (options.checkModuleDeps || (options.dotOutputDir != null &&
-                                      options.verbose == SUMMARY))) {
-            // -dotfile -s prints the configuration of the given root
-            // -checkModuleDeps prints the suggested module-info.java
-            return analyzeModules(dependencyFinder);
+        // check if any module specified in -module is missing
+        Stream.concat(options.addmods.stream(), options.requires.stream())
+              .forEach(mn -> config.findModule(mn).orElseThrow(() ->
+                  new UncheckedBadArgs(new BadArgs("err.module.not.found", mn))));
+
+        // -genmoduleinfo
+        if (options.genModuleInfo != null) {
+            return genModuleInfo(config);
         }
 
-        // otherwise analyze the dependencies
-        if (options.genModuleInfo != null) {
-            return genModuleInfo(dependencyFinder);
-        } else {
-            return analyzeDeps(dependencyFinder);
+        // -check
+        if (options.checkModuleDeps != null) {
+            return new ModuleAnalyzer(config, log, options.checkModuleDeps).run();
         }
+
+        if (options.dotOutputDir != null &&
+                (options.verbose == SUMMARY || options.verbose == MODULE) &&
+                !options.addmods.isEmpty() && inputArgs.isEmpty()) {
+            return new ModuleAnalyzer(config, log).genDotFiles(options.dotOutputDir);
+        }
+
+        return analyzeDeps(config);
     }
 
-    private void buildArchive(DependencyFinder dependencyFinder)
-            throws BadArgs, IOException
-    {
-        // If -genmoduleinfo is specified, the input arguments must be JAR files
-        // Treat them as automatic modules for analysis
-        List<Path> jarfiles = options.genModuleInfo != null
-                                    ?  classes.stream().map(Paths::get)
-                                              .collect(Collectors.toList())
-                                    : Collections.emptyList();
-        // Set module paths
-        this.modulePaths = new ModulePaths(options.upgradeModulePath, options.modulePath, jarfiles);
+    private JdepsConfiguration buildConfig() throws IOException {
+        JdepsConfiguration.Builder builder =
+            new JdepsConfiguration.Builder(options.systemModulePath);
 
-        // add modules to dependency finder for analysis
-        Map<String, Module> modules = modulePaths.getModules();
-        modules.values().stream()
-               .forEach(dependencyFinder::addModule);
+        builder.upgradeModulePath(options.upgradeModulePath)
+               .appModulePath(options.modulePath)
+               .addmods(options.addmods);
 
-        // If -m option is set, add the specified module and its transitive dependences
-        // to the root set
-        if (options.rootModule != null) {
-            modulePaths.dependences(options.rootModule)
-                       .forEach(dependencyFinder::addRoot);
+        if (options.checkModuleDeps != null) {
+            // check all system modules in the image
+            builder.allModules();
         }
 
-        // check if any module specified in -requires is missing
-        Optional<String> req = options.requires.stream()
-                .filter(mn -> !modules.containsKey(mn))
-                .findFirst();
-        if (req.isPresent()) {
-            throw new BadArgs("err.module.not.found", req.get());
-        }
+        if (options.classpath != null)
+            builder.addClassPath(options.classpath);
 
-        // classpath
-        for (Path p : getClassPaths(options.classpath)) {
+        // build the root set of archives to be analyzed
+        for (String s : inputArgs) {
+            Path p = Paths.get(s);
             if (Files.exists(p)) {
-                dependencyFinder.addClassPathArchive(p);
+                builder.addRoot(p);
             }
         }
 
-        // if -genmoduleinfo is not set, the input arguments are considered as
-        // unnamed module.  Add them to the root set
-        if (options.genModuleInfo == null) {
-            // add root set
-            for (String s : classes) {
-                Path p = Paths.get(s);
-                if (Files.exists(p)) {
-                    // add to the initial root set
-                    dependencyFinder.addRoot(p);
-                } else {
-                    if (isValidClassName(s)) {
-                        dependencyFinder.addClassName(s);
-                    } else {
-                        warning("warn.invalid.arg", s);
-                    }
-                }
-            }
-        }
+        return builder.build();
     }
 
-    private boolean analyzeDeps(DependencyFinder dependencyFinder) throws IOException {
-        JdepsFilter filter = dependencyFilter();
-
-        // parse classfiles and find all dependencies
-        findDependencies(dependencyFinder, filter, options.apiOnly);
-
-        // analyze the dependencies collected
-        Analyzer analyzer = new Analyzer(options.verbose, filter);
-        analyzer.run(dependencyFinder.archives());
-
+    private boolean analyzeDeps(JdepsConfiguration config) throws IOException {
         // output result
         final JdepsWriter writer;
         if (options.dotOutputDir != null) {
-            Path dir = Paths.get(options.dotOutputDir);
-            Files.createDirectories(dir);
-            writer = new DotFileWriter(dir, options.verbose,
+            writer = new DotFileWriter(options.dotOutputDir,
+                                       options.verbose,
                                        options.showProfile,
                                        options.showModule,
                                        options.showLabel);
         } else {
-            writer = new SimpleWriter(log, options.verbose,
+            writer = new SimpleWriter(log,
+                                      options.verbose,
                                       options.showProfile,
                                       options.showModule);
         }
 
-        // Targets for reporting - include the root sets and other analyzed archives
-        final List<Archive> targets;
-        if (options.rootModule == null) {
-            // no module as the root set
-            targets = dependencyFinder.archives()
-                                      .filter(filter::accept)
-                                      .filter(a -> !a.getModule().isNamed())
-                                      .collect(Collectors.toList());
-        } else {
-            // named modules in topological order
-            Stream<Module> modules = dependencyFinder.archives()
-                                                     .filter(a -> a.getModule().isNamed())
-                                                     .map(Archive::getModule);
-            Graph<Module> graph = ModuleAnalyzer.graph(modulePaths, modules.toArray(Module[]::new));
-            // then add unnamed module
-            targets = graph.orderedNodes()
-                           .filter(filter::accept)
-                           .collect(Collectors.toList());
+        // analyze the dependencies
+        DepsAnalyzer analyzer = new DepsAnalyzer(config,
+                                        dependencyFilter(config),
+                                        writer,
+                                        options.verbose,
+                                        options.apiOnly);
+
+        boolean ok = analyzer.run(options.compileTimeView, options.depth);
+
+        // print skipped entries, if any
+        analyzer.analyzer.archives()
+            .forEach(archive -> archive.reader()
+                .skippedEntries().stream()
+                .forEach(name -> warning("warn.skipped.entry",
+                                         name, archive.getPathName())));
 
-            // in case any reference not found
-            dependencyFinder.archives()
-                    .filter(a -> !a.getModule().isNamed())
-                    .forEach(targets::add);
-        }
+        if (options.findJDKInternals && !options.nowarning) {
+            Map<String, String> jdkInternals = analyzer.dependences()
+                .collect(Collectors.toMap(Function.identity(), this::replacementFor));
+
+            if (!jdkInternals.isEmpty()) {
+                log.println();
+                warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
 
-        writer.generateOutput(targets, analyzer);
-        if (options.findJDKInternals && !options.nowarning) {
-            showReplacements(targets, analyzer);
+                if (jdkInternals.values().stream().anyMatch(repl -> repl != null)) {
+                    log.println();
+                    log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
+                    log.format("%-40s %s%n", "----------------", "---------------------");
+                        jdkInternals.entrySet().stream()
+                            .filter(e -> e.getValue() != null)
+                            .sorted(Map.Entry.comparingByKey())
+                            .forEach(e -> log.format("%-40s %s%n", e.getKey(), e.getValue()));
+                }
+            }
+
         }
-        return true;
+        return ok;
     }
 
-    private JdepsFilter dependencyFilter() {
+    private boolean genModuleInfo(JdepsConfiguration config) throws IOException {
+        ModuleInfoBuilder builder
+            = new ModuleInfoBuilder(config, inputArgs, options.genModuleInfo);
+        boolean ok = builder.run();
+
+        builder.modules().forEach(module -> {
+            if (module.packages().contains("")) {
+                reportError("ERROR: %s contains unnamed package.  " +
+                    "module-info.java not generated%n", module.getPathName());
+            }
+        });
+
+        if (!ok && !options.nowarning) {
+            log.println("Missing dependencies");
+            builder.visitMissingDeps(
+                new Analyzer.Visitor() {
+                    @Override
+                    public void visitDependence(String origin, Archive originArchive,
+                                                String target, Archive targetArchive) {
+                        if (targetArchive == NOT_FOUND)
+                            log.format("   %-50s -> %-50s %s%n",
+                                origin, target, targetArchive.getName());
+                    }
+                });
+
+            log.println("ERROR: missing dependencies (check \"requires NOT_FOUND;\")");
+        }
+        return ok;
+    }
+
+    /**
+     * Returns a filter used during dependency analysis
+     */
+    private JdepsFilter dependencyFilter(JdepsConfiguration config) {
         // Filter specified by -filter, -package, -regex, and -module options
         JdepsFilter.Builder builder = new JdepsFilter.Builder();
 
-        // Exclude JDK modules from analysis and reporting if -m specified.
-        modulePaths.getModules().values().stream()
-                   .filter(m -> m.isJDK())
-                   .map(Module::name)
-                   .forEach(options.excludes::add);
-
         // source filters
         builder.includePattern(options.includePattern);
-        builder.includeModules(options.includes);
-        builder.excludeModules(options.excludes);
+        builder.includeSystemModules(options.includeSystemModulePattern);
 
         builder.filter(options.filterSamePackage, options.filterSameArchive);
         builder.findJDKInternals(options.findJDKInternals);
 
         // -module
         if (!options.requires.isEmpty()) {
-            Map<String, Module> modules = modulePaths.getModules();
-            builder.packages(options.requires.stream()
-                    .map(modules::get)
-                    .flatMap(m -> m.packages().stream())
-                    .collect(Collectors.toSet()));
+            options.requires.stream()
+                .forEach(mn -> {
+                    Module m = config.findModule(mn).get();
+                    builder.requires(mn, m.packages());
+                });
         }
         // -regex
         if (options.regex != null)
@@ -600,60 +631,12 @@
         if (options.filterRegex != null)
             builder.filter(options.filterRegex);
 
-        return builder.build();
-    }
-
-    private void findDependencies(DependencyFinder dependencyFinder,
-                                  JdepsFilter filter,
-                                  boolean apiOnly)
-        throws IOException
-    {
-        dependencyFinder.findDependencies(filter, apiOnly, options.depth);
-
-        // print skipped entries, if any
-        for (Archive a : dependencyFinder.roots()) {
-            for (String name : a.reader().skippedEntries()) {
-                warning("warn.skipped.entry", name, a.getPathName());
-            }
-        }
-    }
-
-    private boolean genModuleInfo(DependencyFinder dependencyFinder) throws IOException {
-        ModuleInfoBuilder builder = new ModuleInfoBuilder(modulePaths, dependencyFinder);
-        boolean result = builder.run(options.verbose, options.nowarning);
-        builder.build(Paths.get(options.genModuleInfo));
-        return result;
-    }
+        // check if system module is set
+        config.rootModules().stream()
+            .map(Module::name)
+            .forEach(builder::includeIfSystemModule);
 
-    private boolean analyzeModules(DependencyFinder dependencyFinder)
-            throws IOException
-    {
-        ModuleAnalyzer analyzer = new ModuleAnalyzer(modulePaths,
-                                                     dependencyFinder,
-                                                     options.rootModule);
-        if (options.checkModuleDeps) {
-            return analyzer.run();
-        }
-        if (options.dotOutputDir != null && options.verbose == SUMMARY) {
-            Path dir = Paths.get(options.dotOutputDir);
-            Files.createDirectories(dir);
-            analyzer.genDotFile(dir);
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isValidClassName(String name) {
-        if (!Character.isJavaIdentifierStart(name.charAt(0))) {
-            return false;
-        }
-        for (int i=1; i < name.length(); i++) {
-            char c = name.charAt(i);
-            if (c != '.'  && !Character.isJavaIdentifierPart(c)) {
-                return false;
-            }
-        }
-        return true;
+        return builder.build();
     }
 
     public void handleOptions(String[] args) throws BadArgs {
@@ -684,7 +667,7 @@
                     if (name.charAt(0) == '-') {
                         throw new BadArgs("err.option.after.class", name).showUsage(true);
                     }
-                    classes.add(name);
+                    inputArgs.add(name);
                 }
             }
         }
@@ -703,7 +686,7 @@
         log.println(getMessage("error.prefix") + " " + getMessage(key, args));
     }
 
-    private void warning(String key, Object... args) {
+    void warning(String key, Object... args) {
         log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
     }
 
@@ -749,7 +732,7 @@
         boolean version;
         boolean fullVersion;
         boolean showProfile;
-        boolean showModule;
+        boolean showModule = true;
         boolean showSummary;
         boolean apiOnly;
         boolean showLabel;
@@ -761,22 +744,22 @@
         boolean filterSamePackage = true;
         boolean filterSameArchive = false;
         Pattern filterRegex;
-        String dotOutputDir;
-        String genModuleInfo;
-        String classpath = "";
+        Path dotOutputDir;
+        Path genModuleInfo;
+        String classpath;
         int depth = 1;
         Set<String> requires = new HashSet<>();
         Set<String> packageNames = new HashSet<>();
         Pattern regex;             // apply to the dependences
-        Pattern includePattern;    // apply to classes
+        Pattern includePattern;
+        Pattern includeSystemModulePattern;
         boolean compileTimeView = false;
-        boolean checkModuleDeps = false;
+        Set<String> checkModuleDeps;
+        String systemModulePath = System.getProperty("java.home");
         String upgradeModulePath;
         String modulePath;
         String rootModule;
-        // modules to be included or excluded
-        Set<String> includes = new HashSet<>();
-        Set<String> excludes = new HashSet<>();
+        Set<String> addmods = new HashSet<>();
 
         boolean hasFilter() {
             return numFilters() > 0;
@@ -789,11 +772,8 @@
             if (packageNames.size() > 0) count++;
             return count;
         }
+    }
 
-        boolean isRootModule() {
-            return rootModule != null;
-        }
-    }
     private static class ResourceBundleHelper {
         static final ResourceBundle versionRB;
         static final ResourceBundle bundle;
@@ -819,35 +799,6 @@
         }
     }
 
-    /*
-     * Returns the list of Archive specified in cpaths and not included
-     * initialArchives
-     */
-    private List<Path> getClassPaths(String cpaths) throws IOException
-    {
-        if (cpaths.isEmpty()) {
-            return Collections.emptyList();
-        }
-        List<Path> paths = new ArrayList<>();
-        for (String p : cpaths.split(File.pathSeparator)) {
-            if (p.length() > 0) {
-                // wildcard to parse all JAR files e.g. -classpath dir/*
-                int i = p.lastIndexOf(".*");
-                if (i > 0) {
-                    Path dir = Paths.get(p.substring(0, i));
-                    try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
-                        for (Path entry : stream) {
-                            paths.add(entry);
-                        }
-                    }
-                } else {
-                    paths.add(Paths.get(p));
-                }
-            }
-        }
-        return paths;
-    }
-
     /**
      * Returns the recommended replacement API for the given classname;
      * or return null if replacement API is not known.
@@ -865,32 +816,5 @@
             }
         }
         return value;
-    };
-
-    private void showReplacements(List<Archive> archives, Analyzer analyzer) {
-        Map<String,String> jdkinternals = new TreeMap<>();
-        boolean useInternals = false;
-        for (Archive source : archives) {
-            useInternals = useInternals || analyzer.hasDependences(source);
-            for (String cn : analyzer.dependences(source)) {
-                String repl = replacementFor(cn);
-                if (repl != null) {
-                    jdkinternals.putIfAbsent(cn, repl);
-                }
-            }
-        }
-        if (useInternals) {
-            log.println();
-            warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
-        }
-        if (!jdkinternals.isEmpty()) {
-            log.println();
-            log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
-            log.format("%-40s %s%n", "----------------", "---------------------");
-            for (Map.Entry<String,String> e : jdkinternals.entrySet()) {
-                log.format("%-40s %s%n", e.getKey(), e.getValue());
-            }
-        }
     }
-
 }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsWriter.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsWriter.java	Thu May 19 10:55:33 2016 -0700
@@ -24,23 +24,33 @@
  */
 package com.sun.tools.jdeps;
 
+import static com.sun.tools.jdeps.Analyzer.Type.*;
+
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor.Requires;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Map;
 
-import static com.sun.tools.jdeps.Analyzer.Type.*;
+public abstract class JdepsWriter {
+    public static JdepsWriter newDotWriter(Path outputdir, Analyzer.Type type) {
+        return new DotFileWriter(outputdir, type, false, true, false);
+    }
 
-public abstract class JdepsWriter {
+    public static JdepsWriter newSimpleWriter(PrintWriter writer,  Analyzer.Type type) {
+        return new SimpleWriter(writer, type, false, true);
+    }
+
     final Analyzer.Type type;
     final boolean showProfile;
     final boolean showModule;
 
-    JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
+    private JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
         this.type = type;
         this.showProfile = showProfile;
         this.showModule = showModule;
@@ -48,7 +58,7 @@
 
     abstract void generateOutput(Collection<Archive> archives, Analyzer analyzer) throws IOException;
 
-    public static class DotFileWriter extends JdepsWriter {
+    static class DotFileWriter extends JdepsWriter {
         final boolean showLabel;
         final Path outputDir;
         DotFileWriter(Path dir, Analyzer.Type type,
@@ -62,8 +72,10 @@
         void generateOutput(Collection<Archive> archives, Analyzer analyzer)
                 throws IOException
         {
+            Files.createDirectories(outputDir);
+
             // output individual .dot file for each archive
-            if (type != SUMMARY) {
+            if (type != SUMMARY && type != MODULE) {
                 archives.stream()
                         .filter(analyzer::hasDependences)
                         .forEach(archive -> {
@@ -85,13 +97,13 @@
         {
             // If verbose mode (-v or -verbose option),
             // the summary.dot file shows package-level dependencies.
-            Analyzer.Type summaryType =
-                (type == PACKAGE || type == SUMMARY) ? SUMMARY : PACKAGE;
+            boolean isSummary =  type == PACKAGE || type == SUMMARY || type == MODULE;
+            Analyzer.Type summaryType = isSummary ? SUMMARY : PACKAGE;
             Path summary = outputDir.resolve("summary.dot");
             try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
                  SummaryDotFile dotfile = new SummaryDotFile(sw, summaryType)) {
                 for (Archive archive : archives) {
-                    if (type == PACKAGE || type == SUMMARY) {
+                    if (isSummary) {
                         if (showLabel) {
                             // build labels listing package-level dependencies
                             analyzer.visitDependences(archive, dotfile.labelBuilder(), PACKAGE);
@@ -208,19 +220,22 @@
         void generateOutput(Collection<Archive> archives, Analyzer analyzer) {
             RawOutputFormatter depFormatter = new RawOutputFormatter(writer);
             RawSummaryFormatter summaryFormatter = new RawSummaryFormatter(writer);
-            for (Archive archive : archives) {
-                // print summary
-                if (showModule && archive.getModule().isNamed()) {
-                    summaryFormatter.showModuleRequires(archive.getModule());
-                } else {
+            archives.stream()
+                .filter(analyzer::hasDependences)
+                .sorted(Comparator.comparing(Archive::getName))
+                .forEach(archive -> {
+                    if (showModule && archive.getModule().isNamed() && type != SUMMARY) {
+                        // print module-info except -summary
+                        summaryFormatter.printModuleDescriptor(archive.getModule());
+                    }
+                    // print summary
                     analyzer.visitDependences(archive, summaryFormatter, SUMMARY);
-                }
 
-                if (analyzer.hasDependences(archive) && type != SUMMARY) {
-                    // print the class-level or package-level dependences
-                    analyzer.visitDependences(archive, depFormatter);
-                }
-            }
+                    if (analyzer.hasDependences(archive) && type != SUMMARY) {
+                        // print the class-level or package-level dependences
+                        analyzer.visitDependences(archive, depFormatter);
+                    }
+            });
         }
 
         class RawOutputFormatter implements Analyzer.Visitor {
@@ -269,20 +284,16 @@
                 writer.format("%n");
             }
 
-            public void showModuleRequires(Module module) {
+            public void printModuleDescriptor(Module module) {
                 if (!module.isNamed())
                     return;
 
-                writer.format("module %s", module.name());
-                if (module.isAutomatic())
-                    writer.format(" (automatic)");
-                writer.println();
-                module.requires().keySet()
+                writer.format("%s%s%n", module.name(), module.isAutomatic() ? " automatic" : "");
+                writer.format(" [%s]%n", module.location());
+                module.descriptor().requires()
                         .stream()
-                        .sorted()
-                        .forEach(req -> writer.format(" requires %s%s%n",
-                                                      module.requires.get(req) ? "public " : "",
-                                                      req));
+                        .sorted(Comparator.comparing(Requires::name))
+                        .forEach(req -> writer.format("   requires %s%n", req));
             }
         }
     }
@@ -307,7 +318,7 @@
         }
 
         // exported API
-        boolean jdkunsupported = Module.isJDKUnsupported(module, pn);
+        boolean jdkunsupported = Module.JDK_UNSUPPORTED.equals(module.name());
         if (module.isExported(pn) && !jdkunsupported) {
             return showProfileOrModule(module);
         }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Module.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Module.java	Thu May 19 10:55:33 2016 -0700
@@ -25,67 +25,56 @@
 
 package com.sun.tools.jdeps;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UncheckedIOException;
 import java.lang.module.ModuleDescriptor;
 import java.net.URI;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.stream.Collectors;
 
 /**
- * JDeps internal representation of module for dependency analysis.
+ * Jdeps internal representation of module for dependency analysis.
  */
 class Module extends Archive {
-    static final boolean traceOn = Boolean.getBoolean("jdeps.debug");
+    static final Module UNNAMED_MODULE = new UnnamedModule();
+    static final String JDK_UNSUPPORTED = "jdk.unsupported";
+
+    static final boolean DEBUG = Boolean.getBoolean("jdeps.debug");
     static void trace(String fmt, Object... args) {
+        trace(DEBUG, fmt, args);
+    }
+
+    static void trace(boolean traceOn, String fmt, Object... args) {
         if (traceOn) {
             System.err.format(fmt, args);
         }
     }
 
-    /*
-     * Returns true if the given package name is JDK critical internal API
-     * in jdk.unsupported module
-     */
-    static boolean isJDKUnsupported(Module m, String pn) {
-        return JDK_UNSUPPORTED.equals(m.name()) || unsupported.contains(pn);
-    };
+    private final ModuleDescriptor descriptor;
+    private final Map<String, Set<String>> exports;
+    private final boolean isSystem;
+    private final URI location;
 
-    protected final ModuleDescriptor descriptor;
-    protected final Map<String, Boolean> requires;
-    protected final Map<String, Set<String>> exports;
-    protected final Set<String> packages;
-    protected final boolean isJDK;
-    protected final URI location;
+    protected Module(String name) {
+        super(name);
+        this.descriptor = null;
+        this.location = null;
+        this.exports = Collections.emptyMap();
+        this.isSystem = true;
+    }
 
     private Module(String name,
                    URI location,
                    ModuleDescriptor descriptor,
-                   Map<String, Boolean> requires,
                    Map<String, Set<String>> exports,
-                   Set<String> packages,
-                   boolean isJDK,
+                   boolean isSystem,
                    ClassFileReader reader) {
         super(name, location, reader);
         this.descriptor = descriptor;
         this.location = location;
-        this.requires = Collections.unmodifiableMap(requires);
         this.exports = Collections.unmodifiableMap(exports);
-        this.packages = Collections.unmodifiableSet(packages);
-        this.isJDK = isJDK;
+        this.isSystem = isSystem;
     }
 
     /**
@@ -111,31 +100,35 @@
         return descriptor;
     }
 
-    public boolean isJDK() {
-        return isJDK;
+    public URI location() {
+        return location;
     }
 
-    public Map<String, Boolean> requires() {
-        return requires;
+    public boolean isJDK() {
+        String mn = name();
+        return isSystem &&
+            (mn.startsWith("java.") || mn.startsWith("jdk.") || mn.startsWith("javafx."));
+    }
+
+    public boolean isSystem() {
+        return isSystem;
     }
 
     public Map<String, Set<String>> exports() {
         return exports;
     }
 
-    public Map<String, Set<String>> provides() {
-        return descriptor.provides().entrySet().stream()
-                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().providers()));
-    }
-
     public Set<String> packages() {
-        return packages;
+        return descriptor.packages();
     }
 
     /**
      * Tests if the package of the given name is exported.
      */
     public boolean isExported(String pn) {
+        if (JDK_UNSUPPORTED.equals(this.name())) {
+            return false;
+        }
         return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false;
     }
 
@@ -159,11 +152,6 @@
         return isExported(pn) || exports.containsKey(pn) && exports.get(pn).contains(target);
     }
 
-    private final static String JDK_UNSUPPORTED = "jdk.unsupported";
-
-    // temporary until jdk.unsupported module
-    private final static List<String> unsupported = Arrays.asList("sun.misc", "sun.reflect");
-
     @Override
     public String toString() {
         return name();
@@ -171,21 +159,19 @@
 
     public final static class Builder {
         final String name;
-        final Map<String, Boolean> requires = new HashMap<>();
-        final Map<String, Set<String>> exports = new HashMap<>();
-        final Set<String> packages = new HashSet<>();
-        final boolean isJDK;
+        final ModuleDescriptor descriptor;
+        final boolean isSystem;
         ClassFileReader reader;
-        ModuleDescriptor descriptor;
         URI location;
 
-        public Builder(String name) {
-            this(name, false);
+        public Builder(ModuleDescriptor md) {
+            this(md, false);
         }
 
-        public Builder(String name, boolean isJDK) {
-            this.name = name;
-            this.isJDK = isJDK;
+        public Builder(ModuleDescriptor md, boolean isSystem) {
+            this.name = md.name();
+            this.descriptor = md;
+            this.isSystem = isSystem;
         }
 
         public Builder location(URI location) {
@@ -193,48 +179,30 @@
             return this;
         }
 
-        public Builder descriptor(ModuleDescriptor md) {
-            this.descriptor = md;
-            return this;
-        }
-
-        public Builder require(String d, boolean reexport) {
-            requires.put(d, reexport);
-            return this;
-        }
-
-        public Builder packages(Set<String> pkgs) {
-            packages.addAll(pkgs);
-            return this;
-        }
-
-        public Builder export(String p, Set<String> ms) {
-            Objects.requireNonNull(p);
-            Objects.requireNonNull(ms);
-            exports.put(p, new HashSet<>(ms));
-            return this;
-        }
         public Builder classes(ClassFileReader reader) {
             this.reader = reader;
             return this;
         }
 
         public Module build() {
-            if (descriptor.isAutomatic() && isJDK) {
+            if (descriptor.isAutomatic() && isSystem) {
                 throw new InternalError("JDK module: " + name + " can't be automatic module");
             }
 
-            return new Module(name, location, descriptor, requires, exports, packages, isJDK, reader);
+            Map<String, Set<String>> exports = new HashMap<>();
+
+            descriptor.exports().stream()
+                .forEach(exp -> exports.computeIfAbsent(exp.source(), _k -> new HashSet<>())
+                                    .addAll(exp.targets()));
+
+            return new Module(name, location, descriptor, exports, isSystem, reader);
         }
     }
 
-    final static Module UNNAMED_MODULE = new UnnamedModule();
     private static class UnnamedModule extends Module {
         private UnnamedModule() {
             super("unnamed", null, null,
                   Collections.emptyMap(),
-                  Collections.emptyMap(),
-                  Collections.emptySet(),
                   false, null);
         }
 
@@ -260,10 +228,7 @@
     }
 
     private static class StrictModule extends Module {
-        private static final String SERVICES_PREFIX = "META-INF/services/";
-        private final Map<String, Set<String>> provides;
-        private final Module module;
-        private final JarFile jarfile;
+        private final ModuleDescriptor md;
 
         /**
          * Converts the given automatic module to a strict module.
@@ -272,114 +237,26 @@
          * declare service providers, if specified in META-INF/services configuration file
          */
         private StrictModule(Module m, Map<String, Boolean> requires) {
-            super(m.name(), m.location, m.descriptor, requires, m.exports, m.packages, m.isJDK, m.reader());
-            this.module = m;
-            try {
-                this.jarfile = new JarFile(m.path().toFile(), false);
-            } catch (IOException e) {
-                throw new UncheckedIOException(e);
-            }
-            this.provides = providers(jarfile);
+            super(m.name(), m.location, m.descriptor, m.exports, m.isSystem, m.reader());
+
+            ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(m.name());
+            requires.keySet().forEach(mn -> {
+                if (requires.get(mn).equals(Boolean.TRUE)) {
+                    builder.requires(ModuleDescriptor.Requires.Modifier.PUBLIC, mn);
+                } else {
+                    builder.requires(mn);
+                }
+            });
+            m.descriptor.exports().forEach(e -> builder.exports(e));
+            m.descriptor.uses().forEach(s -> builder.uses(s));
+            m.descriptor.provides().values().forEach(p -> builder.provides(p));
+            builder.conceals(m.descriptor.conceals());
+            this.md = builder.build();
         }
 
         @Override
-        public Map<String, Set<String>> provides() {
-            return provides;
-        }
-
-        private Map<String, Set<String>> providers(JarFile jf) {
-            Map<String, Set<String>> provides = new HashMap<>();
-            // map names of service configuration files to service names
-            Set<String> serviceNames =  jf.stream()
-                    .map(e -> e.getName())
-                    .filter(e -> e.startsWith(SERVICES_PREFIX))
-                    .distinct()
-                    .map(this::toServiceName)
-                    .filter(Optional::isPresent)
-                    .map(Optional::get)
-                    .collect(Collectors.toSet());
-
-            // parse each service configuration file
-            for (String sn : serviceNames) {
-                JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
-                Set<String> providerClasses = new HashSet<>();
-                try (InputStream in = jf.getInputStream(entry)) {
-                    BufferedReader reader
-                            = new BufferedReader(new InputStreamReader(in, "UTF-8"));
-                    String cn;
-                    while ((cn = nextLine(reader)) != null) {
-                        if (isJavaIdentifier(cn)) {
-                            providerClasses.add(cn);
-                        }
-                    }
-                } catch (IOException e) {
-                    throw new UncheckedIOException(e);
-                }
-                if (!providerClasses.isEmpty())
-                    provides.put(sn, providerClasses);
-            }
-
-            return provides;
-        }
-
-        /**
-         * Returns a container with the service type corresponding to the name of
-         * a services configuration file.
-         *
-         * For example, if called with "META-INF/services/p.S" then this method
-         * returns a container with the value "p.S".
-         */
-        private Optional<String> toServiceName(String cf) {
-            assert cf.startsWith(SERVICES_PREFIX);
-            int index = cf.lastIndexOf("/") + 1;
-            if (index < cf.length()) {
-                String prefix = cf.substring(0, index);
-                if (prefix.equals(SERVICES_PREFIX)) {
-                    String sn = cf.substring(index);
-                    if (isJavaIdentifier(sn))
-                        return Optional.of(sn);
-                }
-            }
-            return Optional.empty();
-        }
-
-        /**
-         * Reads the next line from the given reader and trims it of comments and
-         * leading/trailing white space.
-         *
-         * Returns null if the reader is at EOF.
-         */
-        private String nextLine(BufferedReader reader) throws IOException {
-            String ln = reader.readLine();
-            if (ln != null) {
-                int ci = ln.indexOf('#');
-                if (ci >= 0)
-                    ln = ln.substring(0, ci);
-                ln = ln.trim();
-            }
-            return ln;
-        }
-
-        /**
-         * Returns {@code true} if the given identifier is a legal Java identifier.
-         */
-        private static boolean isJavaIdentifier(String id) {
-            int n = id.length();
-            if (n == 0)
-                return false;
-            if (!Character.isJavaIdentifierStart(id.codePointAt(0)))
-                return false;
-            int cp = id.codePointAt(0);
-            int i = Character.charCount(cp);
-            for (; i < n; i += Character.charCount(cp)) {
-                cp = id.codePointAt(i);
-                if (!Character.isJavaIdentifierPart(cp) && id.charAt(i) != '.')
-                    return false;
-            }
-            if (cp == '.')
-                return false;
-
-            return true;
+        public ModuleDescriptor descriptor() {
+            return md;
         }
     }
 }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java	Thu May 19 10:55:33 2016 -0700
@@ -24,277 +24,364 @@
  */
 package com.sun.tools.jdeps;
 
-import java.io.IOException;
-import java.io.PrintStream;
-import java.io.UncheckedIOException;
-
+import static com.sun.tools.jdeps.Graph.*;
+import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
+import static com.sun.tools.jdeps.Module.*;
 import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
+import static java.util.stream.Collectors.*;
 
-import java.lang.module.Configuration;
+import com.sun.tools.classfile.Dependency;
+import com.sun.tools.jdeps.JdepsTask.BadArgs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleReference;
-import java.lang.module.ResolvedModule;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
-import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import static com.sun.tools.jdeps.Module.*;
-import static com.sun.tools.jdeps.ModulePaths.SystemModulePath.JAVA_BASE;
-
 /**
  * Analyze module dependences and compare with module descriptor.
  * Also identify any qualified exports not used by the target module.
  */
-class ModuleAnalyzer {
-    private final ModulePaths modulePaths;
+public class ModuleAnalyzer {
+    private static final String JAVA_BASE = "java.base";
+
+    private final JdepsConfiguration configuration;
+    private final PrintWriter log;
+
     private final DependencyFinder dependencyFinder;
-    private final Module root;
-    private final Set<Module> modules;
-    private final Set<String> requiresPublic = new HashSet<>();
-    private final Set<String> requires = new HashSet<>();
-    private final Set<Module> exportTargets = new HashSet<>();
-    private final JdepsFilter filter;
-    private Graph<Module> graph;
-    ModuleAnalyzer(ModulePaths modulePaths, DependencyFinder finder,
-                   String moduleName) {
-        this.modulePaths = modulePaths;
-        this.dependencyFinder = finder;
-        this.root = modulePaths.getModules().get(moduleName);
-        this.modules = modulePaths.dependences(moduleName);
+    private final Map<Module, ModuleDeps> modules;
+
+    public ModuleAnalyzer(JdepsConfiguration config,
+                          PrintWriter log) {
+        this(config, log, Collections.emptySet());
+    }
+    public ModuleAnalyzer(JdepsConfiguration config,
+                          PrintWriter log,
+                          Set<String> names) {
 
-        root.exports().values().stream()
-             .flatMap(Set::stream)
-             .map(target -> modulePaths.getModules().get(target))
-             .forEach(this.exportTargets::add);
+        if (!config.initialArchives().isEmpty()) {
+            String list = config.initialArchives().stream()
+                .map(Archive::getPathName).collect(joining(" "));
+            throw new JdepsTask.UncheckedBadArgs(new BadArgs("err.invalid.module.option",
+                list, "-check"));
+        }
+
+        this.configuration = config;
+        this.log = log;
 
-        this.filter = new JdepsFilter.Builder().filter(true, true).build();
+        this.dependencyFinder = new DependencyFinder(config, DEFAULT_FILTER);
+        if (names.isEmpty()) {
+            this.modules = configuration.rootModules().stream()
+                .collect(toMap(Function.identity(), ModuleDeps::new));
+        } else {
+            this.modules = names.stream()
+                .map(configuration::findModule)
+                .flatMap(Optional::stream)
+                .collect(toMap(Function.identity(), ModuleDeps::new));
+        }
     }
 
-    /**
-     * Returns a graph of transitive closure of the given modules.
-     *
-     * This method does not add the implicit read edges and is intended to
-     * get all transitive closures in (reverse) topological sort.
-     */
-    public static Graph<Module> graph(ModulePaths modulePaths, Module... modules) {
-        Graph.Builder<Module> gb = new Graph.Builder<>();
-        for (Module module : modules) {
-            module.descriptor().requires().stream()
-                    .map(ModuleDescriptor.Requires::name)
-                    .map(mn -> modulePaths.getModules().get(mn))
-                    .forEach(m -> {
-                        gb.addNode(m);
-                        gb.addEdge(module, m);
-                    });
-        }
-        return gb.build();
-    }
+    public boolean run() throws IOException {
+        try {
+            // compute "requires public" dependences
+            modules.values().forEach(ModuleDeps::computeRequiresPublic);
 
-    /**
-     * Do the analysis
-     */
-    public boolean run() {
-        try {
-            computeRequiresPublic();
-            computeRequires();
-            // apply transitive reduction and reports recommended requires.
-            analyzeDeps();
-            // detect any qualiifed exports not used by the target module
-            checkQualifiedExports();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
+            modules.values().forEach(md -> {
+                // compute "requires" dependences
+                md.computeRequires();
+                // apply transitive reduction and reports recommended requires.
+                md.analyzeDeps();
+            });
+        } finally {
+            dependencyFinder.shutdown();
         }
         return true;
     }
 
-    /**
-     * Compute 'requires public' dependences by analyzing API dependencies
-     */
-    private void computeRequiresPublic() throws IOException {
-        JdepsFilter.Builder builder = new JdepsFilter.Builder();
-        // only analyze exported API
-        root.descriptor.exports().stream()
-                .filter(exp -> !exp.isQualified())
-                .map(ModuleDescriptor.Exports::source)
-                .forEach(builder::includePackage);
+    class ModuleDeps {
+        final Module root;
+        Set<Module> requiresPublic;
+        Set<Module> requires;
+        Map<String, Set<String>> unusedQualifiedExports;
+
+        ModuleDeps(Module root) {
+            this.root = root;
+        }
+
+        /**
+         * Compute 'requires public' dependences by analyzing API dependencies
+         */
+        private void computeRequiresPublic() {
+            // record requires public
+            this.requiresPublic = computeRequires(true)
+                .filter(m -> !m.name().equals(JAVA_BASE))
+                .collect(toSet());
+
+            trace("requires public: %s%n", requiresPublic);
+        }
+
+        private void computeRequires() {
+            this.requires = computeRequires(false).collect(toSet());
+            trace("requires: %s%n", requires);
+        }
+
+        private Stream<Module> computeRequires(boolean apionly) {
+            // analyze all classes
+
+            if (apionly) {
+                dependencyFinder.parseExportedAPIs(Stream.of(root));
+            } else {
+                dependencyFinder.parse(Stream.of(root));
+            }
+
+            // find the modules of all the dependencies found
+            return dependencyFinder.getDependences(root)
+                        .map(Archive::getModule);
+        }
+
+        ModuleDescriptor descriptor() {
+            return descriptor(requiresPublic, requires);
+        }
+
+        private ModuleDescriptor descriptor(Set<Module> requiresPublic,
+                                            Set<Module> requires) {
+
+            ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(root.name());
+
+            if (!root.name().equals(JAVA_BASE))
+                builder.requires(MANDATED, JAVA_BASE);
+
+            requiresPublic.stream()
+                .filter(m -> !m.name().equals(JAVA_BASE))
+                .map(Module::name)
+                .forEach(mn -> builder.requires(PUBLIC, mn));
 
-        JdepsFilter filter = builder.filter(true, true).build();
+            requires.stream()
+                .filter(m -> !requiresPublic.contains(m))
+                .filter(m -> !m.name().equals(JAVA_BASE))
+                .map(Module::name)
+                .forEach(mn -> builder.requires(mn));
+
+            return builder.build();
+        }
+
+        ModuleDescriptor reduced() {
+            Graph.Builder<Module> bd = new Graph.Builder<>();
+            requiresPublic.stream()
+                .forEach(m -> {
+                    bd.addNode(m);
+                    bd.addEdge(root, m);
+                });
+
+            // requires public graph
+            Graph<Module> rbg = bd.build().reduce();
+
+            // transitive reduction
+            Graph<Module> newGraph = buildGraph(requires).reduce(rbg);
+            if (DEBUG) {
+                System.err.println("after transitive reduction: ");
+                newGraph.printGraph(log);
+            }
+
+            return descriptor(requiresPublic, newGraph.adjacentNodes(root));
+        }
+
 
-        // analyze dependences for exported packages
-        dependencyFinder.findDependencies(filter, true /* api only */, 1);
-        Analyzer analyzer = new Analyzer(Analyzer.Type.CLASS, filter);
-        analyzer.run(modules);
+        /**
+         * Apply transitive reduction on the resulting graph and reports
+         * recommended requires.
+         */
+        private void analyzeDeps() {
+            Graph.Builder<Module> builder = new Graph.Builder<>();
+            requiresPublic.stream()
+                .forEach(m -> {
+                    builder.addNode(m);
+                    builder.addEdge(root, m);
+                });
+
+            // requires public graph
+            Graph<Module> rbg = buildGraph(requiresPublic).reduce();
+
+            // transitive reduction
+            Graph<Module> newGraph = buildGraph(requires).reduce(builder.build().reduce());
+            if (DEBUG) {
+                System.err.println("after transitive reduction: ");
+                newGraph.printGraph(log);
+            }
+
+            printModuleDescriptor(log, root);
+
+            ModuleDescriptor analyzedDescriptor = descriptor();
+            if (!matches(root.descriptor(), analyzedDescriptor)) {
+                log.format("  [Suggested module descriptor for %s]%n", root.name());
+                analyzedDescriptor.requires()
+                    .stream()
+                    .sorted(Comparator.comparing(ModuleDescriptor.Requires::name))
+                    .forEach(req -> log.format("    requires %s;%n", req));
+            }
 
-        // record requires public
-        analyzer.requires(root)
-                .filter(m -> m != JAVA_BASE && m != root)
-                .map(Archive::getName)
-                .forEach(requiresPublic::add);
-        trace("requires public: %s%n", requiresPublic);
-    }
+            ModuleDescriptor reduced = reduced();
+            if (!matches(root.descriptor(), reduced)) {
+                log.format("  [Transitive reduced graph for %s]%n", root.name());
+                reduced.requires()
+                    .stream()
+                    .sorted(Comparator.comparing(ModuleDescriptor.Requires::name))
+                    .forEach(req -> log.format("    requires %s;%n", req));
+            }
+
+            checkQualifiedExports();
+            log.println();
+        }
+
+        private void checkQualifiedExports() {
+            // detect any qualified exports not used by the target module
+            unusedQualifiedExports = unusedQualifiedExports();
+            if (!unusedQualifiedExports.isEmpty())
+                log.format("  [Unused qualified exports in %s]%n", root.name());
+
+            unusedQualifiedExports.keySet().stream()
+                .sorted()
+                .forEach(pn -> log.format("    exports %s to %s%n", pn,
+                    unusedQualifiedExports.get(pn).stream()
+                        .sorted()
+                        .collect(joining(","))));
+        }
 
-    private void computeRequires() throws IOException {
-        // add the exportTargets of the qualified exports to the root set
-        exportTargets.stream()
-                .peek(target -> trace("add root: %s%n", target))
-                .forEach(dependencyFinder::addRoot);
+        private void printModuleDescriptor(PrintWriter out, Module module) {
+            ModuleDescriptor descriptor = module.descriptor();
+            out.format("%s (%s)%n", descriptor.name(), module.location());
+
+            if (descriptor.name().equals(JAVA_BASE))
+                return;
+
+            out.println("  [Module descriptor]");
+            descriptor.requires()
+                .stream()
+                .sorted(Comparator.comparing(ModuleDescriptor.Requires::name))
+                .forEach(req -> out.format("    requires %s;%n", req));
+        }
+
+
+        /**
+         * Returns a graph of modules required by the specified module.
+         *
+         * Requires public edges of the dependences are added to the graph.
+         */
+        private Graph<Module> buildGraph(Set<Module> deps) {
+            Graph.Builder<Module> builder = new Graph.Builder<>();
+            builder.addNode(root);
+            Set<Module> visited = new HashSet<>();
+            visited.add(root);
+            Deque<Module> deque = new LinkedList<>();
+            deps.stream()
+                .forEach(m -> {
+                    deque.add(m);
+                    builder.addEdge(root, m);
+                });
 
-        // analyze all classes
-        dependencyFinder.findDependencies(filter, false /* all classes */, 1);
-        Analyzer analyzer = new Analyzer(Analyzer.Type.CLASS, filter);
-        analyzer.run(modules);
+            // read requires public from ModuleDescription
+            Module source;
+            while ((source = deque.poll()) != null) {
+                if (visited.contains(source))
+                    continue;
+
+                visited.add(source);
+                builder.addNode(source);
+                Module from = source;
+                source.descriptor().requires().stream()
+                    .filter(req -> req.modifiers().contains(PUBLIC))
+                    .map(ModuleDescriptor.Requires::name)
+                    .map(configuration::findModule)
+                    .flatMap(Optional::stream)
+                    .forEach(m -> {
+                        deque.add(m);
+                        builder.addEdge(from, m);
+                    });
+            }
+            return builder.build();
+        }
+
+        /**
+         * Detects any qualified exports not used by the target module.
+         */
+        private Map<String, Set<String>> unusedQualifiedExports() {
+            Map<String, Set<String>> unused = new HashMap<>();
 
-        // record requires
-        analyzer.requires(root)
-                .filter(m -> m != JAVA_BASE && m != root)
-                .map(Archive::getName)
-                .forEach(requires::add);
+            // build the qualified exports map
+            Map<String, Set<String>> qualifiedExports =
+                root.exports().entrySet().stream()
+                    .filter(e -> !e.getValue().isEmpty())
+                    .map(Map.Entry::getKey)
+                    .collect(toMap(Function.identity(), _k -> new HashSet<>()));
+
+            Set<Module> mods = new HashSet<>();
+            root.exports().values()
+                .stream()
+                .flatMap(Set::stream)
+                .forEach(target -> configuration.findModule(target)
+                    .ifPresentOrElse(mods::add,
+                        () -> log.format("Warning: %s not found%n", target))
+                );
+
+            // parse all target modules
+            dependencyFinder.parse(mods.stream());
 
-        this.graph = buildGraph(analyzer, root);
-        if (traceOn) {
-            trace("dependences: %s%n", graph.nodes());
-            graph.printGraph(System.out);
+            // adds to the qualified exports map if a module references it
+            mods.stream().forEach(m ->
+                m.getDependencies()
+                    .map(Dependency.Location::getPackageName)
+                    .filter(qualifiedExports::containsKey)
+                    .forEach(pn -> qualifiedExports.get(pn).add(m.name())));
+
+            // compare with the exports from ModuleDescriptor
+            Set<String> staleQualifiedExports =
+                qualifiedExports.keySet().stream()
+                    .filter(pn -> !qualifiedExports.get(pn).equals(root.exports().get(pn)))
+                    .collect(toSet());
+
+            if (!staleQualifiedExports.isEmpty()) {
+                for (String pn : staleQualifiedExports) {
+                    Set<String> targets = new HashSet<>(root.exports().get(pn));
+                    targets.removeAll(qualifiedExports.get(pn));
+                    unused.put(pn, targets);
+                }
+            }
+            return unused;
         }
     }
 
-    /**
-     * Apply transitive reduction on the resulting graph and reports
-     * recommended requires.
-     */
-    private void analyzeDeps() {
-        String moduleName = root.name();
-
-        Graph.Builder<String> builder = new Graph.Builder<>();
-        requiresPublic.stream()
-                .forEach(mn -> {
-                    builder.addNode(mn);
-                    builder.addEdge(moduleName, mn);
-                });
-        // requires public graph
-        Graph<String> rbg = builder.build().reduce();
-
-        // convert the dependence graph from Module to name
-        Set<String> nodes = this.graph.nodes().stream()
-                .map(Module::name)
-                .collect(Collectors.toSet());
-        Map<String, Set<String>> edges = new HashMap<>();
-        this.graph.edges().keySet().stream()
-                .forEach(m -> {
-                    String mn = m.name();
-                    Set<String> es = edges.computeIfAbsent(mn, _k -> new HashSet<String>());
-                    this.graph.edges().get(m).stream()
-                            .map(Module::name)
-                            .forEach(es::add);
-                });
-
-        // transitive reduction
-        Graph<String> newGraph = new Graph<>(nodes, edges).reduce(rbg);
-        if (traceOn) {
-            System.out.println("after transitive reduction");
-            newGraph.printGraph(System.out);
-        };
+    private boolean matches(ModuleDescriptor md, ModuleDescriptor other) {
+        // build requires public from ModuleDescriptor
+        Set<ModuleDescriptor.Requires> reqPublic = md.requires().stream()
+            .filter(req -> req.modifiers().contains(PUBLIC))
+            .collect(toSet());
+        Set<ModuleDescriptor.Requires> otherReqPublic = other.requires().stream()
+            .filter(req -> req.modifiers().contains(PUBLIC))
+            .collect(toSet());
 
-        Set<String> reducedRequires = newGraph.adjacentNodes(moduleName);
-        if (matches(root.descriptor(), requires, requiresPublic) &&
-                matches(root.descriptor(), reducedRequires, requiresPublic)) {
-            System.out.println("--- Analysis result: no change for " + root.name());
-        } else {
-            System.out.println("--- Analysis result: suggested requires for " + root.name());
-            System.out.format("module %s%n", root.name());
-                requires.stream()
-                        .sorted()
-                        .forEach(dn -> System.out.format("  requires %s%s;%n",
-                                requiresPublic.contains(dn) ? "public " : "", dn));
-            if (!requires.equals(reducedRequires) && !reducedRequires.isEmpty()) {
-                System.out.format("%nmodule %s (reduced)%n", root.name());
-                newGraph.adjacentNodes(moduleName)
-                     .stream()
-                     .sorted()
-                     .forEach(dn -> System.out.format("  requires %s%s;%n",
-                                requiresPublic.contains(dn) ? "public " : "", dn));
-            }
-            System.out.println("\n---  Module descriptor");
-            Graph<Module> mdGraph = graph(modulePaths, root);
-            mdGraph.reverse(m -> printModuleDescriptor(System.out, m.descriptor()));
-        }
-    }
-
-    /**
-     * Detects any qualified exports not used by the target module.
-     */
-    private void checkQualifiedExports() throws IOException {
-        Analyzer analyzer = new Analyzer(Analyzer.Type.CLASS, filter);
-        analyzer.run(dependencyFinder.roots());
-
-        // build the qualified exports map
-        Map<String, Set<String>> qualifiedExports =
-            root.exports().entrySet().stream()
-                .filter(e -> !e.getValue().isEmpty())
-                .map(Map.Entry::getKey)
-                .collect(Collectors.toMap(Function.identity(), _k -> new HashSet<>()));
-
-        // adds to the qualified exports map if a module references it
-        for (Module m : exportTargets) {
-            analyzer.dependences(m).stream()
-                    .map(this::toPackageName)
-                    .filter(qualifiedExports::containsKey)
-                    .forEach(pn -> qualifiedExports.get(pn).add(m.name()));
-        }
-
-        // compare with the exports from ModuleDescriptor
-        Set<String> staleQualifiedExports =
-            qualifiedExports.keySet().stream()
-                .filter(pn -> !qualifiedExports.get(pn).equals(root.exports().get(pn)))
-                .collect(Collectors.toSet());
-
-        if (!staleQualifiedExports.isEmpty()) {
-            System.out.println("--- Unused qualified exports in " + root.name());
-            for (String pn : staleQualifiedExports) {
-                Set<String> unused = new HashSet<>(root.exports().get(pn));
-                unused.removeAll(qualifiedExports.get(pn));
-                System.out.format("  exports %s to %s%n", pn,
-                                  unused.stream().collect(Collectors.joining(",")));
-            }
-        }
-    }
-
-    private String toPackageName(String cn) {
-        int i = cn.lastIndexOf('.');
-        return i > 0 ? cn.substring(0, i) : "";
-    }
-
-    private boolean matches(ModuleDescriptor md, Set<String> requires, Set<String> requiresPublic) {
-        Set<String> reqPublic = md.requires().stream()
-                .filter(req -> req.modifiers().contains(PUBLIC))
-                .map(ModuleDescriptor.Requires::name)
-                .collect(Collectors.toSet());
-        if (!requiresPublic.equals(reqPublic)) {
+        if (!reqPublic.equals(otherReqPublic)) {
             trace("mismatch requires public: %s%n", reqPublic);
             return false;
         }
-        // java.base is not in requires
-        int javaBase = md.name().equals(JAVA_BASE.name()) ? 0 : 1;
-        if (requires.size()+javaBase != md.requires().size()) {
-            trace("mismatch requires: %d != %d%n", requires.size()+1, md.requires().size());
-            return false;
-        }
 
-        Set<String> unused = md.requires().stream()
-                 .map(ModuleDescriptor.Requires::name)
-                 .filter(req -> !requires.contains(req) && !req.equals(JAVA_BASE.name()))
-                 .collect(Collectors.toSet());
+        Set<ModuleDescriptor.Requires> unused = md.requires().stream()
+            .filter(req -> !other.requires().contains(req))
+            .collect(Collectors.toSet());
+
         if (!unused.isEmpty()) {
             trace("mismatch requires: %s%n", unused);
             return false;
@@ -302,371 +389,50 @@
         return true;
     }
 
-    private void printModuleDescriptor(PrintStream out, ModuleDescriptor descriptor) {
-        if (descriptor.name().equals("java.base"))
-            return;
-
-        out.format("module %s%n", descriptor.name());
-        descriptor.requires()
-                .stream()
-                .sorted(Comparator.comparing(ModuleDescriptor.Requires::name))
-                .forEach(req -> out.format("  requires %s;%n", req));
-    }
-
     /**
-     * Returns a graph of modules required by the specified module.
+     * Generate dotfile from module descriptor
      *
-     * Requires public edges of the dependences are added to the graph.
+     * @param dir output directory
      */
-    private Graph<Module> buildGraph(Analyzer analyzer, Module module) {
-        Graph.Builder<Module> builder = new Graph.Builder<>();
-        builder.addNode(module);
-        Set<Module> visited = new HashSet<>();
-        visited.add(module);
-        Deque<Module> deque = new LinkedList<>();
-        analyzer.requires(module)
-                .map(Archive::getModule)
-                .filter(m -> m != JAVA_BASE)
-                .forEach(m -> {
-                    deque.add(m);
-                    builder.addEdge(module, m);
-                });
-
-        // read requires public from ModuleDescription
-        Module source;
-        while ((source = deque.poll()) != null) {
-            if (visited.contains(source))
-                continue;
-            visited.add(source);
-            builder.addNode(source);
-            Module from = source;
-            source.descriptor().requires().stream()
-                    .filter(req -> req.modifiers().contains(PUBLIC))
-                    .map(ModuleDescriptor.Requires::name)
-                    .map(req -> modulePaths.getModules().get(req))
-                    .filter(m -> m != JAVA_BASE)
-                    .forEach(m -> {
-                        deque.add(m);
-                        builder.addEdge(from, m);
-                    });
+    public boolean genDotFiles(Path dir) throws IOException {
+        Files.createDirectories(dir);
+        for (Module m : modules.keySet()) {
+            genDotFile(dir, m.name());
         }
-        return builder.build();
+        return true;
     }
 
-    static class Graph<T> {
-        private final Set<T> nodes;
-        private final Map<T, Set<T>> edges;
 
-        private Graph(Set<T> nodes, Map<T, Set<T>> edges) {
-            this.nodes = nodes;
-            this.edges = edges;
-        }
-
-        public Set<T> nodes() {
-            return nodes;
-        }
-
-        public Map<T, Set<T>> edges() {
-            return edges;
-        }
-
-        public Set<T> adjacentNodes(T u) {
-            return edges.get(u);
-        }
-
-        /**
-         * Returns a new Graph after transitive reduction
-         */
-        public Graph<T> reduce() {
-            Graph.Builder<T> builder = new Builder<>();
-            nodes.stream()
-                    .forEach(u -> {
-                        builder.addNode(u);
-                        edges.get(u).stream()
-                                .filter(v -> !pathExists(u, v, false))
-                                .forEach(v -> builder.addEdge(u, v));
-                    });
-            return builder.build();
-        }
-
-        /**
-         * Returns a new Graph after transitive reduction.  All edges in
-         * the given g takes precedence over this graph.
-         *
-         * @throw IllegalArgumentException g must be a subgraph this graph
-         */
-        public Graph<T> reduce(Graph<T> g) {
-            boolean subgraph = nodes.containsAll(g.nodes) && g.edges.keySet().stream()
-                    .allMatch(u -> adjacentNodes(u).containsAll(g.adjacentNodes(u)));
-            if (!subgraph) {
-                throw new IllegalArgumentException(g + " is not a subgraph of " + this);
-            }
-
-            Graph.Builder<T> builder = new Builder<>();
-            nodes.stream()
-                    .forEach(u -> {
-                        builder.addNode(u);
-                        // filter the edge if there exists a path from u to v in the given g
-                        // or there exists another path from u to v in this graph
-                        edges.get(u).stream()
-                                .filter(v -> !g.pathExists(u, v) && !pathExists(u, v, false))
-                                .forEach(v -> builder.addEdge(u, v));
-                    });
-
-            // add the overlapped edges from this graph and the given g
-            g.edges().keySet().stream()
-                    .forEach(u -> g.adjacentNodes(u).stream()
-                                    .filter(v -> isAdjacent(u, v))
-                                    .forEach(v -> builder.addEdge(u, v)));
-            return builder.build();
-        }
-
-        /**
-         * Returns nodes sorted in topological order.
-         */
-        public Stream<T> orderedNodes() {
-            TopoSorter<T> sorter = new TopoSorter<>(this);
-            return sorter.result.stream();
-        }
-
-        /**
-         * Iterates the nodes sorted in topological order and performs the
-         * given action.
-         */
-        public void ordered(Consumer<T> action) {
-            TopoSorter<T> sorter = new TopoSorter<>(this);
-            sorter.ordered(action);
-        }
-
-        /**
-         * Iterates the nodes sorted in reverse topological order and
-         * performs the given action.
-         */
-        public void reverse(Consumer<T> action) {
-            TopoSorter<T> sorter = new TopoSorter<>(this);
-            sorter.reverse(action);
-        }
-
-        private boolean isAdjacent(T u, T v) {
-            return edges.containsKey(u) && edges.get(u).contains(v);
-        }
-
-        private boolean pathExists(T u, T v) {
-            return pathExists(u, v, true);
-        }
-
-        /**
-         * Returns true if there exists a path from u to v in this graph.
-         * If includeAdjacent is false, it returns true if there exists
-         * another path from u to v of distance > 1
-         */
-        private boolean pathExists(T u, T v, boolean includeAdjacent) {
-            if (!nodes.contains(u) || !nodes.contains(v)) {
-                return false;
-            }
-            if (includeAdjacent && isAdjacent(u, v)) {
-                return true;
-            }
-            Deque<T> stack = new LinkedList<>();
-            Set<T> visited = new HashSet<>();
-            stack.push(u);
-            while (!stack.isEmpty()) {
-                T node = stack.pop();
-                if (node.equals(v)) {
-                    return true;
-                }
-                if (!visited.contains(node)) {
-                    visited.add(node);
-                    edges.get(node).stream()
-                            .filter(e -> includeAdjacent || !node.equals(u) || !e.equals(v))
-                            .forEach(e -> stack.push(e));
-                }
-            }
-            assert !visited.contains(v);
-            return false;
-        }
-
-        void printGraph(PrintStream out) {
-            out.println("graph for " + nodes);
-            nodes.stream()
-                 .forEach(u -> adjacentNodes(u).stream()
-                                   .forEach(v -> out.format("%s -> %s%n", u, v)));
-        }
-
-        @Override
-        public String toString() {
-            return nodes.toString();
-        }
-
-        static class Builder<T> {
-            final Set<T> nodes = new HashSet<>();
-            final Map<T, Set<T>> edges = new HashMap<>();
-
-            public void addNode(T node) {
-                if (nodes.contains(node)) {
-                    return;
-                }
-                nodes.add(node);
-                edges.computeIfAbsent(node, _e -> new HashSet<>());
-            }
-
-            public void addEdge(T u, T v) {
-                addNode(u);
-                addNode(v);
-                edges.get(u).add(v);
-            }
-
-            public Graph<T> build() {
-                return new Graph<>(nodes, edges);
-            }
-
-            void print(PrintStream out) {
-                out.println(nodes);
-                nodes.stream()
-                        .forEach(u -> edges.get(u).stream()
-                                        .forEach(v -> out.format("%s -> %s%n", u, v)));
-            }
-        }
-    }
-
-    static class TopoSorter<T> {
-        final Deque<T> result = new LinkedList<>();
-        final Deque<T> nodes;
-        final Graph<T> graph;
-        TopoSorter(Graph<T> graph) {
-            this.graph = graph;
-            this.nodes = new LinkedList<>(graph.nodes);
-            sort();
-        }
-
-        public void ordered(Consumer<T> action) {
-            result.iterator().forEachRemaining(action);
-        }
-
-        public void reverse(Consumer<T> action) {
-            result.descendingIterator().forEachRemaining(action);
-        }
-
-        private void sort() {
-            Deque<T> visited = new LinkedList<>();
-            Deque<T> done = new LinkedList<>();
-            T node;
-            while ((node = nodes.poll()) != null) {
-                if (!visited.contains(node)) {
-                    visit(node, visited, done);
-                }
-            }
-        }
-
-        private void visit(T node, Deque<T> visited, Deque<T> done) {
-            if (visited.contains(node)) {
-                if (!done.contains(node)) {
-                    throw new IllegalArgumentException("Cyclic detected: " +
-                            node + " " + graph.edges().get(node));
-                }
-                return;
-            }
-            visited.add(node);
-            graph.edges().get(node).stream()
-                 .forEach(x -> visit(x, visited, done));
-            done.add(node);
-            result.addLast(node);
-        }
-    }
-
-    static class DotGraph {
-        static final String ORANGE = "#e76f00";
-        static final String BLUE = "#437291";
-        static final String GRAY = "#dddddd";
-
-        static final String REEXPORTS = "";
-        static final String REQUIRES = "style=\"dashed\"";
-        static final String REQUIRES_BASE = "color=\"" + GRAY + "\"";
-
-        static final Set<String> javaModules = modules(name ->
-                (name.startsWith("java.") && !name.equals("java.smartcardio")));
-        static final Set<String> jdkModules = modules(name ->
-                (name.startsWith("java.") ||
-                        name.startsWith("jdk.") ||
-                        name.startsWith("javafx.")) && !javaModules.contains(name));
-
-        private static Set<String> modules(Predicate<String> predicate) {
-            return ModuleFinder.ofSystem().findAll()
-                               .stream()
-                               .map(ModuleReference::descriptor)
-                               .map(ModuleDescriptor::name)
-                               .filter(predicate)
-                               .collect(Collectors.toSet());
-        }
-
-        static void printAttributes(PrintStream out) {
-            out.format("  size=\"25,25\";%n");
-            out.format("  nodesep=.5;%n");
-            out.format("  ranksep=1.5;%n");
-            out.format("  pencolor=transparent;%n");
-            out.format("  node [shape=plaintext, fontname=\"DejaVuSans\", fontsize=36, margin=\".2,.2\"];%n");
-            out.format("  edge [penwidth=4, color=\"#999999\", arrowhead=open, arrowsize=2];%n");
-        }
-
-        static void printNodes(PrintStream out, Graph<String> graph) {
-            out.format("  subgraph se {%n");
-            graph.nodes().stream()
-                 .filter(javaModules::contains)
-                 .forEach(mn -> out.format("  \"%s\" [fontcolor=\"%s\", group=%s];%n",
-                                           mn, ORANGE, "java"));
-            out.format("  }%n");
-            graph.nodes().stream()
-                 .filter(jdkModules::contains)
-                 .forEach(mn -> out.format("    \"%s\" [fontcolor=\"%s\", group=%s];%n",
-                                              mn, BLUE, "jdk"));
-
-            graph.nodes().stream()
-                    .filter(mn -> !javaModules.contains(mn) && !jdkModules.contains(mn))
-                    .forEach(mn -> out.format("  \"%s\";%n", mn));
-        }
-
-        static void printEdges(PrintStream out, Graph<String> graph,
-                               String node, Set<String> requiresPublic) {
-            graph.adjacentNodes(node).forEach(dn -> {
-                String attr = dn.equals("java.base") ? REQUIRES_BASE
-                        : (requiresPublic.contains(dn) ? REEXPORTS : REQUIRES);
-                out.format("  \"%s\" -> \"%s\" [%s];%n", node, dn, attr);
-            });
-        }
-    }
-
-    public void genDotFile(Path dir) throws IOException {
-        String name = root.name();
-        try (PrintStream out
-                     = new PrintStream(Files.newOutputStream(dir.resolve(name + ".dot")))) {
-            Configuration cf = modulePaths.configuration(name);
+    private void genDotFile(Path dir, String name) throws IOException {
+        try (OutputStream os = Files.newOutputStream(dir.resolve(name + ".dot"));
+             PrintWriter out = new PrintWriter(os)) {
+            Set<Module> modules = configuration.resolve(Set.of(name))
+                .collect(Collectors.toSet());
 
             // transitive reduction
-            Graph<String> graph = gengraph(cf);
+            Graph<String> graph = gengraph(modules);
 
             out.format("digraph \"%s\" {%n", name);
             DotGraph.printAttributes(out);
             DotGraph.printNodes(out, graph);
 
-            cf.modules().stream()
-                    .map(ResolvedModule::reference)
-                    .map(ModuleReference::descriptor)
-                    .sorted(Comparator.comparing(ModuleDescriptor::name))
-                    .forEach(md -> {
-                String mn = md.name();
-                Set<String> requiresPublic = md.requires().stream()
+            modules.stream()
+                .map(Module::descriptor)
+                .sorted(Comparator.comparing(ModuleDescriptor::name))
+                .forEach(md -> {
+                    String mn = md.name();
+                    Set<String> requiresPublic = md.requires().stream()
                         .filter(d -> d.modifiers().contains(PUBLIC))
                         .map(d -> d.name())
-                        .collect(Collectors.toSet());
+                        .collect(toSet());
 
-                DotGraph.printEdges(out, graph, mn, requiresPublic);
-            });
+                    DotGraph.printEdges(out, graph, mn, requiresPublic);
+                });
 
             out.println("}");
         }
     }
 
-
     /**
      * Returns a Graph of the given Configuration after transitive reduction.
      *
@@ -675,12 +441,12 @@
      * (e.g. U -> V) from being reduced by a path (U -> X -> Y -> V)
      * in which  V would not be re-exported from U.
      */
-    private Graph<String> gengraph(Configuration cf) {
+    private Graph<String> gengraph(Set<Module> modules) {
         // build a Graph containing only requires public edges
         // with transitive reduction.
         Graph.Builder<String> rpgbuilder = new Graph.Builder<>();
-        for (ResolvedModule resolvedModule : cf.modules()) {
-            ModuleDescriptor md = resolvedModule.reference().descriptor();
+        for (Module module : modules) {
+            ModuleDescriptor md = module.descriptor();
             String mn = md.name();
             md.requires().stream()
                     .filter(d -> d.modifiers().contains(PUBLIC))
@@ -692,12 +458,12 @@
 
         // build the readability graph
         Graph.Builder<String> builder = new Graph.Builder<>();
-        for (ResolvedModule resolvedModule : cf.modules()) {
-            ModuleDescriptor md = resolvedModule.reference().descriptor();
+        for (Module module : modules) {
+            ModuleDescriptor md = module.descriptor();
             String mn = md.name();
             builder.addNode(mn);
-            resolvedModule.reads().stream()
-                    .map(ResolvedModule::name)
+            configuration.reads(module)
+                    .map(Module::name)
                     .forEach(d -> builder.addEdge(mn, d));
         }
 
@@ -705,4 +471,25 @@
         return builder.build().reduce(rpg);
     }
 
+    // ---- for testing purpose
+    public ModuleDescriptor[] descriptors(String name) {
+        ModuleDeps moduleDeps = modules.keySet().stream()
+            .filter(m -> m.name().equals(name))
+            .map(modules::get)
+            .findFirst().get();
+
+        ModuleDescriptor[] descriptors = new ModuleDescriptor[3];
+        descriptors[0] = moduleDeps.root.descriptor();
+        descriptors[1] = moduleDeps.descriptor();
+        descriptors[2] = moduleDeps.reduced();
+        return descriptors;
+    }
+
+    public Map<String, Set<String>> unusedQualifiedExports(String name) {
+        ModuleDeps moduleDeps = modules.keySet().stream()
+            .filter(m -> m.name().equals(name))
+            .map(modules::get)
+            .findFirst().get();
+        return moduleDeps.unusedQualifiedExports;
+    }
 }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java	Thu May 19 10:55:33 2016 -0700
@@ -24,15 +24,24 @@
  */
 package com.sun.tools.jdeps;
 
-import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
+import static com.sun.tools.jdeps.JdepsTask.*;
 import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
-import static com.sun.tools.jdeps.Module.trace;
+import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.ModuleDescriptor.Provides;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleFinder;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
@@ -40,168 +49,159 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+
 public class ModuleInfoBuilder {
-    final ModulePaths modulePaths;
+    final JdepsConfiguration configuration;
+    final Path outputdir;
+
     final DependencyFinder dependencyFinder;
-    final JdepsFilter filter;
     final Analyzer analyzer;
-    final Map<Module, Module> strictModules = new HashMap<>();
-    ModuleInfoBuilder(ModulePaths modulePaths, DependencyFinder finder) {
-        this.modulePaths = modulePaths;
-        this.dependencyFinder = finder;
-        this.filter = new JdepsFilter.Builder().filter(true, true).build();
-        this.analyzer = new Analyzer(CLASS, filter);
+    final Map<Module, Module> strictModules;
+    public ModuleInfoBuilder(JdepsConfiguration configuration,
+                             List<String> args,
+                             Path outputdir) {
+        this.configuration = configuration;
+        this.outputdir = outputdir;
+
+        this.dependencyFinder = new DependencyFinder(configuration, DEFAULT_FILTER);
+        this.analyzer = new Analyzer(configuration, Analyzer.Type.CLASS, DEFAULT_FILTER);
+
+        // add targets to modulepath if it has module-info.class
+        List<Path> paths = args.stream()
+            .map(fn -> Paths.get(fn))
+            .collect(Collectors.toList());
+
+        // automatic module to convert to strict module
+        this.strictModules = ModuleFinder.of(paths.toArray(new Path[0]))
+                .findAll().stream()
+                .map(configuration::toModule)
+                .collect(Collectors.toMap(Function.identity(), Function.identity()));
+
+        Optional<Module> om = strictModules.keySet().stream()
+                                    .filter(m -> !m.descriptor().isAutomatic())
+                                    .findAny();
+        if (om.isPresent()) {
+            throw new UncheckedBadArgs(new BadArgs("err.genmoduleinfo.not.jarfile",
+                                                   om.get().getPathName()));
+        }
+        if (strictModules.isEmpty()) {
+            throw new UncheckedBadArgs(new BadArgs("err.invalid.path", args));
+        }
     }
 
-    private Stream<Module> automaticModules() {
-        return modulePaths.getModules().values()
-                .stream()
-                .filter(Module::isAutomatic);
+    public boolean run() throws IOException {
+        try {
+            // pass 1: find API dependencies
+            Map<Archive, Set<Archive>> requiresPublic = computeRequiresPublic();
+
+            // pass 2: analyze all class dependences
+            dependencyFinder.parse(automaticModules().stream());
+
+            analyzer.run(automaticModules(), dependencyFinder.locationToArchive());
+
+            // computes requires and requires public
+            automaticModules().forEach(m -> {
+                Map<String, Boolean> requires;
+                if (requiresPublic.containsKey(m)) {
+                    requires = requiresPublic.get(m).stream()
+                        .map(Archive::getModule)
+                        .collect(Collectors.toMap(Module::name, (v) -> Boolean.TRUE));
+                } else {
+                    requires = new HashMap<>();
+                }
+                analyzer.requires(m)
+                    .map(Archive::getModule)
+                    .forEach(d -> requires.putIfAbsent(d.name(), Boolean.FALSE));
+
+                strictModules.put(m, m.toStrictModule(requires));
+            });
+
+            // generate module-info.java
+            descriptors().forEach(md -> writeModuleInfo(outputdir, md));
+
+            // find any missing dependences
+            return automaticModules().stream()
+                        .flatMap(analyzer::requires)
+                        .allMatch(m -> !m.equals(NOT_FOUND));
+        } finally {
+            dependencyFinder.shutdown();
+        }
+    }
+
+    /**
+     * Returns the stream of resulting modules
+     */
+    Stream<Module> modules() {
+        return strictModules.values().stream();
+    }
+
+    /**
+     * Returns the stream of resulting ModuleDescriptors
+     */
+    public Stream<ModuleDescriptor> descriptors() {
+        return strictModules.values().stream().map(Module::descriptor);
+    }
+
+    void visitMissingDeps(Analyzer.Visitor visitor) {
+        automaticModules().stream()
+            .filter(m -> analyzer.requires(m).anyMatch(d -> d.equals(NOT_FOUND)))
+            .forEach(m -> {
+                analyzer.visitDependences(m, visitor, Analyzer.Type.VERBOSE);
+            });
+    }
+    void writeModuleInfo(Path dir, ModuleDescriptor descriptor) {
+        String mn = descriptor.name();
+        Path srcFile = dir.resolve(mn).resolve("module-info.java");
+        try {
+            Files.createDirectories(srcFile.getParent());
+            System.out.println("writing to " + srcFile);
+            try (PrintWriter pw = new PrintWriter(Files.newOutputStream(srcFile))) {
+                printModuleInfo(pw, descriptor);
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private void printModuleInfo(PrintWriter writer, ModuleDescriptor descriptor) {
+        writer.format("module %s {%n", descriptor.name());
+
+        Map<String, Module> modules = configuration.getModules();
+        // first print the JDK modules
+        descriptor.requires().stream()
+                  .filter(req -> !req.name().equals("java.base"))   // implicit requires
+                  .sorted(Comparator.comparing(Requires::name))
+                  .forEach(req -> writer.format("    requires %s;%n", req));
+
+        descriptor.exports().stream()
+                  .peek(exp -> {
+                      if (exp.targets().size() > 0)
+                          throw new InternalError(descriptor.name() + " qualified exports: " + exp);
+                  })
+                  .sorted(Comparator.comparing(Exports::source))
+                  .forEach(exp -> writer.format("    exports %s;%n", exp.source()));
+
+        descriptor.provides().values().stream()
+                    .sorted(Comparator.comparing(Provides::service))
+                    .forEach(p -> p.providers().stream()
+                        .sorted()
+                        .forEach(impl -> writer.format("    provides %s with %s;%n", p.service(), impl)));
+
+        writer.println("}");
+    }
+
+
+    private Set<Module> automaticModules() {
+        return strictModules.keySet();
     }
 
     /**
      * Compute 'requires public' dependences by analyzing API dependencies
      */
-    Map<Module, Set<Module>> computeRequiresPublic() throws IOException {
-        dependencyFinder.findDependencies(filter, true /* api only */, 1);
-        Analyzer pass1 = new Analyzer(Analyzer.Type.CLASS, filter);
-
-        pass1.run(dependencyFinder.archives());
-
-        return automaticModules().collect(Collectors.toMap(Function.identity(),
-                source -> pass1.requires(source)
-                               .map(Archive::getModule)
-                               .collect(Collectors.toSet())));
-    }
-
-    boolean run(Analyzer.Type verbose, boolean quiet) throws IOException {
-        // add all automatic modules to the root set
-        automaticModules().forEach(dependencyFinder::addRoot);
-
-        // pass 1: find API dependencies
-        Map<Module, Set<Module>> requiresPublic = computeRequiresPublic();
-
-        // pass 2: analyze all class dependences
-        dependencyFinder.findDependencies(filter, false /* all classes */, 1);
-        analyzer.run(dependencyFinder.archives());
-
-        // computes requires and requires public
-        automaticModules().forEach(m -> {
-            Map<String, Boolean> requires;
-            if (requiresPublic.containsKey(m)) {
-                requires = requiresPublic.get(m)
-                        .stream()
-                        .collect(Collectors.toMap(Archive::getName, (v) -> Boolean.TRUE));
-            } else {
-                requires = new HashMap<>();
-            }
-            analyzer.requires(m)
-                    .forEach(d -> requires.putIfAbsent(d.getName(), Boolean.FALSE));
-
-            trace("strict module %s requires %s%n", m.name(), requires);
-            strictModules.put(m, m.toStrictModule(requires));
-        });
-
-        // find any missing dependences
-        Optional<Module> missingDeps = automaticModules()
-                .filter(this::missingDep)
-                .findAny();
-        if (missingDeps.isPresent()) {
-            automaticModules()
-                    .filter(this::missingDep)
-                    .forEach(m -> {
-                        System.err.format("Missing dependencies from %s%n", m.name());
-                        analyzer.visitDependences(m,
-                                new Analyzer.Visitor() {
-                                    @Override
-                                    public void visitDependence(String origin, Archive originArchive,
-                                                                String target, Archive targetArchive) {
-                                        if (targetArchive == NOT_FOUND)
-                                            System.err.format("   %-50s -> %-50s %s%n",
-                                                    origin, target, targetArchive.getName());
-                                    }
-                                }, verbose);
-                        System.err.println();
-                    });
-
-            System.err.println("ERROR: missing dependencies (check \"requires NOT_FOUND;\")");
-        }
-        return missingDeps.isPresent() ? false : true;
-    }
+    private Map<Archive, Set<Archive>> computeRequiresPublic() throws IOException {
+        // parse the input modules
+        dependencyFinder.parseExportedAPIs(automaticModules().stream());
 
-    private boolean missingDep(Archive m) {
-        return analyzer.requires(m).filter(a -> a.equals(NOT_FOUND))
-                       .findAny().isPresent();
-    }
-
-    void build(Path dir) throws IOException {
-        ModuleInfoWriter writer = new ModuleInfoWriter(dir);
-        writer.generateOutput(strictModules.values(), analyzer);
-    }
-
-    private class ModuleInfoWriter {
-        private final Path outputDir;
-        ModuleInfoWriter(Path dir) {
-            this.outputDir = dir;
-        }
-
-        void generateOutput(Iterable<Module> modules, Analyzer analyzer) throws IOException {
-            // generate module-info.java file for each archive
-            for (Module m : modules) {
-                if (m.packages().contains("")) {
-                    System.err.format("ERROR: %s contains unnamed package.  " +
-                                      "module-info.java not generated%n", m.getPathName());
-                    continue;
-                }
-
-                String mn = m.getName();
-                Path srcFile = outputDir.resolve(mn).resolve("module-info.java");
-                Files.createDirectories(srcFile.getParent());
-                System.out.println("writing to " + srcFile);
-                try (PrintWriter pw = new PrintWriter(Files.newOutputStream(srcFile))) {
-                    printModuleInfo(pw, m);
-                }
-            }
-        }
-
-        private void printModuleInfo(PrintWriter writer, Module m) {
-            writer.format("module %s {%n", m.name());
-
-            Map<String, Module> modules = modulePaths.getModules();
-            Map<String, Boolean> requires = m.requires();
-            // first print the JDK modules
-            requires.keySet().stream()
-                    .filter(mn -> !mn.equals("java.base"))   // implicit requires
-                    .filter(mn -> modules.containsKey(mn) && modules.get(mn).isJDK())
-                    .sorted()
-                    .forEach(mn -> {
-                        String modifier = requires.get(mn) ? "public " : "";
-                        writer.format("    requires %s%s;%n", modifier, mn);
-                    });
-
-            // print requires non-JDK modules
-            requires.keySet().stream()
-                    .filter(mn -> !modules.containsKey(mn) || !modules.get(mn).isJDK())
-                    .sorted()
-                    .forEach(mn -> {
-                        String modifier = requires.get(mn) ? "public " : "";
-                        writer.format("    requires %s%s;%n", modifier, mn);
-                    });
-
-            m.packages().stream()
-                    .sorted()
-                    .forEach(pn -> writer.format("    exports %s;%n", pn));
-
-            m.provides().entrySet().stream()
-                    .sorted(Map.Entry.comparingByKey())
-                    .forEach(e -> {
-                        String service = e.getKey();
-                        e.getValue().stream()
-                                .sorted()
-                                .forEach(impl -> writer.format("    provides %s with %s;%n", service, impl));
-                    });
-
-            writer.println("}");
-        }
+        return dependencyFinder.dependences();
     }
 }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModulePaths.java	Thu May 19 10:55:33 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-/*
- * Copyright (c) 2012, 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 com.sun.tools.jdeps;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.lang.module.Configuration;
-import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleReference;
-import java.lang.module.ResolvedModule;
-import java.net.URI;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystemNotFoundException;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.ProviderNotFoundException;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
-
-public class ModulePaths {
-    final ModuleFinder finder;
-    final Map<String, Module> modules = new LinkedHashMap<>();
-
-    public ModulePaths(String upgradeModulePath, String modulePath) {
-        this(upgradeModulePath, modulePath, Collections.emptyList());
-    }
-
-    public ModulePaths(String upgradeModulePath, String modulePath, List<Path> jars) {
-        ModuleFinder finder = ModuleFinder.ofSystem();
-        if (upgradeModulePath != null) {
-            finder = ModuleFinder.compose(createModulePathFinder(upgradeModulePath), finder);
-        }
-        if (jars.size() > 0) {
-            finder = ModuleFinder.compose(finder, ModuleFinder.of(jars.toArray(new Path[0])));
-        }
-        if (modulePath != null) {
-            finder = ModuleFinder.compose(finder, createModulePathFinder(modulePath));
-        }
-        this.finder = finder;
-
-        // add modules from modulepaths
-        finder.findAll().stream().forEach(mref ->
-            modules.computeIfAbsent(mref.descriptor().name(), mn -> toModule(mn, mref))
-        );
-    }
-
-    /**
-     * Returns the list of Modules that can be found in the specified
-     * module paths.
-     */
-    Map<String, Module> getModules() {
-        return modules;
-    }
-
-    Set<Module> dependences(String... roots) {
-        Configuration cf = configuration(roots);
-        return cf.modules().stream()
-                .map(ResolvedModule::name)
-                .map(modules::get)
-                .collect(Collectors.toSet());
-    }
-
-    Configuration configuration(String... roots) {
-        return Configuration.empty().resolveRequires(finder, ModuleFinder.empty(), Set.of(roots));
-    }
-
-    private static ModuleFinder createModulePathFinder(String mpaths) {
-        if (mpaths == null) {
-            return null;
-        } else {
-            String[] dirs = mpaths.split(File.pathSeparator);
-            Path[] paths = new Path[dirs.length];
-            int i = 0;
-            for (String dir : dirs) {
-                paths[i++] = Paths.get(dir);
-            }
-            return ModuleFinder.of(paths);
-        }
-    }
-
-    private static Module toModule(String mn, ModuleReference mref) {
-        return SystemModulePath.find(mn)
-                               .orElse(toModule(new Module.Builder(mn), mref));
-    }
-
-    private static Module toModule(Module.Builder builder, ModuleReference mref) {
-        ModuleDescriptor md = mref.descriptor();
-        builder.descriptor(md);
-        for (ModuleDescriptor.Requires req : md.requires()) {
-            builder.require(req.name(), req.modifiers().contains(PUBLIC));
-        }
-        for (ModuleDescriptor.Exports exp : md.exports()) {
-            builder.export(exp.source(), exp.targets());
-        }
-        builder.packages(md.packages());
-
-        try {
-            URI location = mref.location()
-                               .orElseThrow(FileNotFoundException::new);
-            builder.location(location);
-            builder.classes(getClassReader(location, md.name()));
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-        return builder.build();
-    }
-
-    static class SystemModulePath {
-        final static Module JAVA_BASE;
-
-        private final static FileSystem fs;
-        private final static Path root;
-        private final static Map<String, Module> installed = new HashMap<>();
-        static {
-            if (isJrtAvailable()) {
-                // jrt file system
-                fs = FileSystems.getFileSystem(URI.create("jrt:/"));
-                root = fs.getPath("/modules");
-            } else {
-                // exploded image
-                String javahome = System.getProperty("java.home");
-                fs = FileSystems.getDefault();
-                root = Paths.get(javahome, "modules");
-            }
-
-            ModuleFinder.ofSystem().findAll().stream()
-                 .forEach(mref ->
-                     installed.computeIfAbsent(mref.descriptor().name(),
-                                               mn -> toModule(new Module.Builder(mn, true), mref))
-                 );
-            JAVA_BASE = installed.get("java.base");
-
-            Profile.init(installed);
-        }
-
-        private static boolean isJrtAvailable() {
-            try {
-                FileSystems.getFileSystem(URI.create("jrt:/"));
-                return true;
-            } catch (ProviderNotFoundException | FileSystemNotFoundException e) {
-                return false;
-            }
-        }
-
-        public static Optional<Module> find(String mn) {
-            return installed.containsKey(mn) ? Optional.of(installed.get(mn))
-                                             : Optional.empty();
-        }
-
-        public static boolean contains(Module m) {
-            return installed.containsValue(m);
-        }
-
-        public static ClassFileReader getClassReader(String modulename) throws IOException {
-            Path mp = root.resolve(modulename);
-            if (Files.exists(mp) && Files.isDirectory(mp)) {
-                return ClassFileReader.newInstance(fs, mp);
-            } else {
-                throw new FileNotFoundException(mp.toString());
-            }
-        }
-    }
-
-    /**
-     * Returns a ModuleClassReader that only reads classes for the given modulename.
-     */
-    public static ClassFileReader getClassReader(URI location, String modulename)
-            throws IOException {
-        if (location.getScheme().equals("jrt")) {
-            return SystemModulePath.getClassReader(modulename);
-        } else {
-            Path path = Paths.get(location);
-            return ClassFileReader.newInstance(path);
-        }
-    }
-}
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Profile.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Profile.java	Thu May 19 10:55:33 2016 -0700
@@ -26,9 +26,13 @@
 package com.sun.tools.jdeps;
 
 import java.io.IOException;
+import java.lang.module.ModuleDescriptor;
+import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -44,17 +48,18 @@
     // need a way to determine JRE modules
     SE_JRE("Java SE JRE", 4, "java.se", "jdk.charsets",
                             "jdk.crypto.ec", "jdk.crypto.pkcs11",
-                            "jdk.crypto.mscapi", "jdk.crypto.ucrypto", "jdk.jvmstat",
+                            "jdk.crypto.mscapi", "jdk.crypto.ucrypto",
                             "jdk.localedata", "jdk.scripting.nashorn", "jdk.zipfs"),
     FULL_JRE("Full JRE", 5, "java.se.ee", "jdk.charsets",
                             "jdk.crypto.ec", "jdk.crypto.pkcs11",
                             "jdk.crypto.mscapi", "jdk.crypto.ucrypto", "jdk.jvmstat",
-                            "jdk.localedata", "jdk.scripting.nashorn", "jdk.zipfs");
+                            "jdk.localedata", "jdk.scripting.nashorn",
+                            "jdk.unsupported", "jdk.zipfs");
 
     final String name;
     final int profile;
     final String[] mnames;
-    final Set<Module> modules = new HashSet<>();
+    final Map<String, Module> modules = new HashMap<>();
 
     Profile(String name, int profile, String... mnames) {
         this.name = name;
@@ -75,12 +80,18 @@
         return JDK.isEmpty() ? 0 : Profile.values().length;
     }
 
+    Optional<Module> findModule(String name) {
+        return modules.containsKey(name)
+            ? Optional.of(modules.get(name))
+            : Optional.empty();
+    }
+
     /**
      * Returns the Profile for the given package name; null if not found.
      */
     public static Profile getProfile(String pn) {
         for (Profile p : Profile.values()) {
-            for (Module m : p.modules) {
+            for (Module m : p.modules.values()) {
                 if (m.packages().contains(pn)) {
                     return p;
                 }
@@ -94,7 +105,7 @@
      */
     public static Profile getProfile(Module m) {
         for (Profile p : Profile.values()) {
-            if (p.modules.contains(m)) {
+            if (p.modules.containsValue(m)) {
                 return p;
             }
         }
@@ -102,34 +113,28 @@
     }
 
     private final static Set<Module> JDK = new HashSet<>();
-    static synchronized void init(Map<String, Module> installed) {
-        for (Profile p : Profile.values()) {
-            for (String mn : p.mnames) {
-                // this includes platform-dependent module that may not exist
-                Module m = installed.get(mn);
-                if (m != null) {
-                    p.addModule(installed, m);
-                }
-            }
-        }
+    static synchronized void init(Map<String, Module> systemModules) {
+        Arrays.stream(Profile.values()).forEach(p ->
+            // this includes platform-dependent module that may not exist
+            Arrays.stream(p.mnames)
+                  .filter(systemModules::containsKey)
+                  .map(systemModules::get)
+                  .forEach(m -> p.addModule(systemModules, m)));
 
         // JDK modules should include full JRE plus other jdk.* modules
         // Just include all installed modules.  Assume jdeps is running
         // in JDK image
-        JDK.addAll(installed.values());
+        JDK.addAll(systemModules.values());
     }
 
-    private void addModule(Map<String, Module> installed, Module m) {
-        modules.add(m);
-        for (String n : m.requires().keySet()) {
-            Module d = installed.get(n);
-            if (d == null) {
-                throw new InternalError("module " + n + " required by " +
-                        m.name() + " doesn't exist");
-            }
-            modules.add(d);
-        }
+    private void addModule(Map<String, Module> systemModules, Module module) {
+        modules.put(module.name(), module);
+        module.descriptor().requires().stream()
+              .map(ModuleDescriptor.Requires::name)
+              .map(systemModules::get)
+              .forEach(m -> modules.put(m.name(), m));
     }
+
     // for debugging
     public static void main(String[] args) throws IOException {
         // find platform modules
@@ -139,14 +144,6 @@
         for (Profile p : Profile.values()) {
             String profileName = p.name;
             System.out.format("%2d: %-10s  %s%n", p.profile, profileName, p.modules);
-            for (Module m: p.modules) {
-                System.out.format("module %s%n", m.name());
-                System.out.format("   requires %s%n", m.requires());
-                for (Map.Entry<String,Set<String>> e: m.exports().entrySet()) {
-                    System.out.format("   exports %s %s%n", e.getKey(),
-                        e.getValue().isEmpty() ? "" : "to " + e.getValue());
-                }
-            }
         }
         System.out.println("All JDK modules:-");
         JDK.stream().sorted(Comparator.comparing(Module::name))
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Thu May 19 10:55:33 2016 -0700
@@ -1,12 +1,10 @@
 main.usage.summary=\
-Usage: {0} <options> [-m <module-name> | <classes...>]\n\
+Usage: {0} <options> <path ...>]\n\
 use -h, -? or -help for a list of possible options
 
 main.usage=\
-Usage: {0} <options> [-m <module-name> | <classes...>]\n\
-If -m <module-name> is specified, the specified module will be analyzed\n\
-otherwise, <classes> can be a pathname to a .class file, a directory,\n\
-a JAR file, or a fully-qualified class name.\n\
+Usage: {0} <options> <path ...>]\n\
+<path> can be a pathname to a .class file, a directory, a JAR file.\n\
 \n\
 Possible options include:
 
@@ -14,134 +12,138 @@
 warn.prefix=Warning:
 
 main.opt.h=\
-\  -h -?        -help                   Print this usage message
+\  -h -?        -help                Print this usage message
 
 main.opt.version=\
-\  -version                             Version information
+\  -version                          Version information
 
 main.opt.v=\
-\  -v           -verbose                Print all class level dependencies\n\
-\                                       Equivalent to -verbose:class -filter:none.\n\
-\  -verbose:package                     Print package-level dependencies excluding\n\
-\                                       dependencies within the same package by default\n\
-\  -verbose:class                       Print class-level dependencies excluding\n\
-\                                       dependencies within the same package by default
-
-main.opt.f=\
-\  -f <regex>  -filter <regex>          Filter dependences matching the given pattern\n\
-\                                       If given multiple times, the last one will be used.\n\
-\  -filter:package                      Filter dependences within the same package (default)\n\
-\  -filter:module                       Filter dependences within the same module\n\
-\  -filter:archive                      Filter dependences within the same archive\n\
-\  -filter:none                         No -filter:package and -filter:archive filtering\n\
-\                                       Filtering specified via the -filter option still applies.
+\  -v           -verbose             Print all class level dependencies\n\
+\                                    Equivalent to -verbose:class -filter:none.\n\
+\  -verbose:package                  Print package-level dependencies excluding\n\
+\                                    dependencies within the same package by default\n\
+\  -verbose:class                    Print class-level dependencies excluding\n\
+\                                    dependencies within the same package by default
 
 main.opt.s=\
-\  -s           -summary                Print dependency summary only.\n\
-\                                       If -s option is used with -m, the module descriptor of\n\
-\                                       the given module will be read to generate the graph.
+\  -s           -summary             Print dependency summary only.
 
-main.opt.p=\
-\  -p <pkgname>                         Finds dependences matching the given package name\n\
-\  -package <pkgname>                   (may be given multiple times).
+main.opt.f=\
+\  -f <regex>  -filter <regex>       Filter dependences matching the given\n\
+\                                    pattern. If given multiple times, the last\n\
+\                                    one will be used.\n\
+\  -filter:package                   Filter dependences within the same package.\n\
+\                                    This is the default.\n\
+\  -filter:archive                   Filter dependences within the same archive.\n\
+\  -filter:module                    Filter dependences within the same module.\n\
+\  -filter:none                      No -filter:package and -filter:archive\n\
+\                                    filtering.  Filtering specified via the\n\
+\                                    -filter option still applies.\n\
+
+main.opt.p=\n\
+\Options to filter dependencies:\n\
+\  -p <pkgname> -package <pkgname>   Finds dependences matching the given package\n\
+\                                    name (may be given multiple times).
 
 main.opt.e=\
-\  -e <regex>\n\
-\  -regex <regex>                       Finds dependences matching the given pattern.
+\  -e <regex> -regex <regex>         Finds dependences matching the given pattern.
 
 main.opt.module=\
-\  -module <module-name>                Finds dependences matching the given module name\n\
-\                                       (may be given multiple times).\n\
-\                                       -package, -regex, -requires are mutual exclusive.
+\  -module <module-name>             Finds dependences matching the given module\n\
+\                                    name (may be given multiple times).\n\
+\                                    -package, -regex, -module are mutual exclusive.
 
-main.opt.include=\
-\  -include <regex>                     Restrict analysis to classes matching pattern\n\
-\                                       This option filters the list of classes to\n\
-\                                       be analyzed.  It can be used together with\n\
-\                                       -p and -e which apply pattern to the dependences
+main.opt.include=\n\
+ \Options to filter classes to be analyzed:\n\
+\  -include <regex>                  Restrict analysis to classes matching pattern\n\
+\                                    This option filters the list of classes to\n\
+\                                    be analyzed.  It can be used together with\n\
+\                                    -p and -e which apply pattern to the dependences
 
 main.opt.P=\
-\  -P           -profile                Show profile containing a package
-
-main.opt.M=\
-\  -M                                   Show module containing a package
+\  -P           -profile             Show profile containing a package
 
 main.opt.cp=\
-\  -cp <path>   -classpath <path>       Specify where to find class files
+\  -cp <path>   -classpath <path>    Specify where to find class files
 
 main.opt.mp=\
 \  -mp <module path>...\n\
-\  -modulepath <module path>...         Specify module path
+\  -modulepath <module path>...      Specify module path
 
 main.opt.upgrademodulepath=\
 \  -upgrademodulepath <module path>...  Specify upgrade module path
 
 main.opt.m=\
-\  -m <module-name>                     Specify the name of the module and its transitive\n\
-\                                       dependences to be analyzed.
+\  -m <module-name>                  Specify the root module for analysis
 
 main.opt.R=\
-\  -R           -recursive              Recursively traverse all run-time dependencies.\n\
-\                                       The -R option implies -filter:none.  If -p, -e, -f\n\
-\                                       option is specified, only the matching dependences\n\
-\                                       are analyzed.
+\  -R           -recursive           Recursively traverse all run-time dependencies.\n\
+\                                    The -R option implies -filter:none.  If -p,\n\
+\                                    -e, -foption is specified, only the matching\n\
+\                                    dependences are analyzed.
 
 main.opt.ct=\
-\  -ct          -compile-time           Compile-time view of transitive dependencies\n\
-\                                       i.e. compile-time view of -R option.  If a dependence\n\
-\                                       is found from a directory, a JAR file or a module,\n\
-\                                       all class files in that containing archive are analyzed.
+\  -ct          -compile-time        Compile-time view of transitive dependencies\n\
+\                                    i.e. compile-time view of -R option.\n\
+\                                    Analyzes the dependences per other given options\n\
+\                                    If a dependence is found from a directory,\n\
+\                                    a JAR file or a module, all classes in that \n\
+\                                    containing archive are analyzed.
 
 main.opt.apionly=\
-\  -apionly                             Restrict analysis to APIs i.e. dependences\n\
-\                                       from the signature of public and protected\n\
-\                                       members of public classes including field\n\
-\                                       type, method parameter types, returned type,\n\
-\                                       checked exception types etc
+\  -apionly                          Restrict analysis to APIs i.e. dependences\n\
+\                                    from the signature of public and protected\n\
+\                                    members of public classes including field\n\
+\                                    type, method parameter types, returned type,\n\
+\                                    checked exception types etc.
 
 main.opt.genmoduleinfo=\
-\  -genmoduleinfo <dir>                 Generate module-info.java under the specified directory.\n\
-\                                       The specified JAR files will be analyzed.\n\
-\                                       This option cannot be used with -dotoutput or -cp.
+\  -genmoduleinfo <dir>              Generate module-info.java under the specified\n\
+\                                    directory. The specified JAR files will be\n\
+\                                    analyzed. This option cannot be used with\n\
+\                                    -dotoutput or -cp.
 
 main.opt.check=\
-\  -check                               Analyze the dependence of a given module specified via\n\
-\                                       -m option.  It prints out the resulting module dependency\n\
-\                                       graph after transition reduction and also identifies any\n\
-\                                       unused qualified exports.
+\  -check <module-name>[,<module-name>...\n\
+\                                    Analyze the dependence of the specified modules\n\
+\                                    It prints the module descriptor, the resulting\n\
+\                                    module dependences after analysis and the\n\
+\                                    graph after transition reduction.  It also\n\
+\                                    identifies any unused qualified exports.
 
 
 main.opt.dotoutput=\
-\  -dotoutput <dir>                     Destination directory for DOT file output
+\  -dotoutput <dir>                  Destination directory for DOT file output
 
 main.opt.jdkinternals=\
-\  -jdkinternals                        Finds class-level dependences on JDK internal APIs.\n\
-\                                       By default, it analyzes all classes on -classpath\n\
-\                                       and input files unless -include option is specified.\n\
-\                                       This option cannot be used with -p, -e and -s options.\n\
-\                                       WARNING: JDK internal APIs may not be accessible in\n\
-\                                       the next release.
+\  -jdkinternals                     Finds class-level dependences on JDK internal\n\
+\                                    APIs. By default, it analyzes all classes\n\
+\                                    on -classpath and input files unless -include\n\
+\                                    option is specified. This option cannot be\n\
+\                                    used with -p, -e and -s options.\n\
+\                                    WARNING: JDK internal APIs are inaccessible.
 
 main.opt.depth=\
-\  -depth=<depth>                       Specify the depth of the transitive\n\
-\                                       dependency analysis
+\  -depth=<depth>                    Specify the depth of the transitive\n\
+\                                    dependency analysis
 
 main.opt.q=\
-\  -q           -quiet                  Do not show missing dependencies from -genmoduleinfo output.
+\  -q           -quiet               Do not show missing dependencies from \n\
+\                                    -genmoduleinfo output.
 
 err.unknown.option=unknown option: {0}
 err.missing.arg=no value given for {0}
 err.invalid.arg.for.option=invalid argument for option: {0}
 err.option.after.class=option must be specified before classes: {0}
-err.genmoduleinfo.not.jarfile={0} not valid for -genmoduleinfo option (must be JAR file)
+err.genmoduleinfo.not.jarfile={0} not valid for -genmoduleinfo option (must be non-modular JAR file)
 err.profiles.msg=No profile information
 err.exception.message={0}
 err.invalid.path=invalid path: {0}
-err.invalid.module.option=-m {0} is set but {1} is specified.
-err.invalid.filters=Only one of -package (-p), -regex (-e), -requires option can be set
+err.invalid.module.option=Cannot set {0} with {1} option.
+err.invalid.filters=Only one of -package (-p), -regex (-e), -module option can be set
 err.module.not.found=module not found: {0}
-err.root.module.not.set=-m is not set
-warn.invalid.arg=Invalid classname or pathname not exist: {0}
+err.root.module.not.set=root module set empty
+warn.invalid.arg=Path not exist: {0}
 warn.split.package=package {0} defined in {1} {2}
 warn.replace.useJDKInternals=\
 JDK internal APIs are unsupported and private to JDK implementation that are\n\
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties	Thu May 19 10:55:33 2016 -0700
@@ -1,6 +1,5 @@
-// No translation needed
+# No translation needed
 com.sun.crypto.provider.SunJCE=Use java.security.Security.getProvider(provider-name) @since 1.3
-com.sun.image.codec=Use javax.imageio @since 1.4
 com.sun.org.apache.xml.internal.security=Use java.xml.crypto @since 1.6
 com.sun.org.apache.xml.internal.security.utils.Base64=Use java.util.Base64 @since 1.8
 com.sun.org.apache.xml.internal.resolver=Use javax.xml.catalog @since 9
@@ -9,17 +8,34 @@
 com.sun.rowset=Use javax.sql.rowset.RowSetProvider @since 1.7
 com.sun.tools.javac.tree=Use com.sun.source @since 1.6
 com.sun.tools.javac=Use javax.tools and javax.lang.model @since 1.6
+java.awt.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
+java.awt.dnd.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
+jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
 sun.awt.image.codec=Use javax.imageio @since 1.4
-sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
-sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
-sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
-sun.misc.Service=Use java.util.ServiceLoader @since 1.6
+sun.awt.CausedFocusEvent=Use java.awt.event.FocusEvent::getCause @since 9
+sun.font.FontUtilities=See java.awt.Font.textRequiresLayout	@since 9
+sun.reflect.Reflection=See StackWalker API @since 9
+sun.reflect.ReflectionFactory=See http://openjdk.java.net/jeps/260
+sun.misc.Unsafe=See http://openjdk.java.net/jeps/260
+sun.misc.Signal=See http://openjdk.java.net/jeps/260
+sun.misc.SignalHandler=See http://openjdk.java.net/jeps/260
 sun.security.action=Use java.security.PrivilegedAction @since 1.1
 sun.security.krb5=Use com.sun.security.jgss
 sun.security.provider.PolicyFile=Use java.security.Policy.getInstance("JavaPolicy", new URIParameter(uri)) @since 1.6
 sun.security.provider.Sun=Use java.security.Security.getProvider(provider-name) @since 1.3
 sun.security.util.SecurityConstants=Use appropriate java.security.Permission subclass @since 1.1
 sun.security.x509.X500Name=Use javax.security.auth.x500.X500Principal @since 1.4
-sun.tools.jar=Use java.util.jar or jar tool @since 1.2
-jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
+sun.tools.jar=Use java.util.jar or jar tool @since 1.2\
+# Internal APIs removed in JDK 9
+com.apple.eawt=Use java.awt.desktop and JEP 272 @since 9
+com.apple.concurrent=Removed. See https://bugs.openjdk.java.net/browse/JDK-8148187
+com.sun.image.codec=Use javax.imageio @since 1.4
+sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
+sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
+sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
+sun.misc.Service=Use java.util.ServiceLoader @since 1.6
+sun.misc=Removed. See http://openjdk.java.net/jeps/260
+sun.reflect=Removed. See http://openjdk.java.net/jeps/260
 
+
+
--- a/langtools/test/tools/jdeps/APIDeps.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/APIDeps.java	Thu May 19 10:55:33 2016 -0700
@@ -25,6 +25,7 @@
  * @test
  * @bug 8015912 8029216 8048063 8050804
  * @summary Test -apionly and -jdkinternals options
+ * @library lib
  * @modules java.base/sun.security.x509
  *          java.management
  *          jdk.jdeps/com.sun.tools.classfile
@@ -154,7 +155,8 @@
     Map<String,String> jdeps(String... args) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
-        System.err.println("jdeps " + Arrays.toString(args));
+        System.err.println("jdeps " + Arrays.stream(args)
+            .collect(Collectors.joining(" ")));
         int rc = com.sun.tools.jdeps.Main.run(args, pw);
         pw.close();
         String out = sw.toString();
--- a/langtools/test/tools/jdeps/Basic.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/Basic.java	Thu May 19 10:55:33 2016 -0700
@@ -39,6 +39,8 @@
 import java.nio.file.Path;
 import java.util.*;
 import java.util.regex.*;
+import java.util.stream.Collectors;
+
 import static java.nio.file.StandardCopyOption.*;
 
 public class Basic {
@@ -157,7 +159,7 @@
     Map<String,String> jdeps(String... args) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
-        System.err.println("jdeps " + Arrays.toString(args));
+        System.err.println("jdeps " + Arrays.stream(args).collect(Collectors.joining(" ")));
         int rc = com.sun.tools.jdeps.Main.run(args, pw);
         pw.close();
         String out = sw.toString();
--- a/langtools/test/tools/jdeps/CompilerUtils.java	Thu May 19 10:55:33 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 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.
- *
- * 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.
- */
-
-import javax.tools.JavaCompiler;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.StandardLocation;
-import javax.tools.ToolProvider;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public final class CompilerUtils {
-    private CompilerUtils() { }
-
-    /**
-     * Compile all the java sources in {@code <source>/**} to
-     * {@code <destination>/**}. The destination directory will be created if
-     * it doesn't exist.
-     *
-     * All warnings/errors emitted by the compiler are output to System.out/err.
-     *
-     * @return true if the compilation is successful
-     *
-     * @throws IOException if there is an I/O error scanning the source tree or
-     *                     creating the destination directory
-     */
-    public static boolean compile(Path source, Path destination, String... options)
-        throws IOException
-    {
-        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-        StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
-
-        List<Path> sources
-            = Files.find(source, Integer.MAX_VALUE,
-                         (file, attrs) -> (file.toString().endsWith(".java")))
-                   .collect(Collectors.toList());
-
-        Files.createDirectories(destination);
-        jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
-                                 Arrays.asList(destination));
-
-        List<String> opts = Arrays.asList(options);
-        JavaCompiler.CompilationTask task
-            = compiler.getTask(null, jfm, null, opts, null,
-                               jfm.getJavaFileObjectsFromPaths(sources));
-
-        return task.call();
-    }
-
-    /**
-     * Compile the specified module from the given module sourcepath
-     *
-     * All warnings/errors emitted by the compiler are output to System.out/err.
-     *
-     * @return true if the compilation is successful
-     *
-     * @throws IOException if there is an I/O error scanning the source tree or
-     *                     creating the destination directory
-     */
-    public static boolean compileModule(Path source, Path destination,
-                                        String moduleName, String... options) {
-        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-        StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
-
-        try {
-            Files.createDirectories(destination);
-            jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
-                                     Arrays.asList(destination));
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-
-        Stream<String> opts = Arrays.stream(new String[] {
-            "-modulesourcepath", source.toString(), "-m", moduleName
-        });
-        List<String> javacOpts = Stream.concat(opts, Arrays.stream(options))
-                                        .collect(Collectors.toList());
-        JavaCompiler.CompilationTask task
-            = compiler.getTask(null, jfm, null, javacOpts, null, null);
-        return task.call();
-    }
-
-
-    public static void cleanDir(Path dir) throws IOException {
-        if (Files.notExists(dir)) return;
-
-        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
-            @Override
-            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
-                    throws IOException
-            {
-                Files.delete(file);
-                return FileVisitResult.CONTINUE;
-            }
-            @Override
-            public FileVisitResult postVisitDirectory(Path dir, IOException e)
-                    throws IOException
-            {
-                if (e == null) {
-                    Files.delete(dir);
-                    return FileVisitResult.CONTINUE;
-                } else {
-                    // directory iteration failed
-                    throw e;
-                }
-            }
-        });
-        Files.deleteIfExists(dir);
-    }
-}
--- a/langtools/test/tools/jdeps/DotFileTest.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/DotFileTest.java	Thu May 19 10:55:33 2016 -0700
@@ -41,6 +41,7 @@
 import java.nio.file.Paths;
 import java.util.*;
 import java.util.regex.*;
+import java.util.stream.Collectors;
 
 public class DotFileTest {
     public static void main(String... args) throws Exception {
@@ -182,7 +183,7 @@
         // invoke jdeps
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
-        System.err.println("jdeps " + args);
+        System.err.println("jdeps " + args.stream().collect(Collectors.joining(" ")));
         int rc = com.sun.tools.jdeps.Main.run(args.toArray(new String[0]), pw);
         pw.close();
         String out = sw.toString();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/lib/CompilerUtils.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+import javax.tools.JavaCompiler;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public final class CompilerUtils {
+    private CompilerUtils() { }
+
+    /**
+     * Compile all the java sources in {@code <source>/**} to
+     * {@code <destination>/**}. The destination directory will be created if
+     * it doesn't exist.
+     *
+     * All warnings/errors emitted by the compiler are output to System.out/err.
+     *
+     * @return true if the compilation is successful
+     *
+     * @throws IOException if there is an I/O error scanning the source tree or
+     *                     creating the destination directory
+     */
+    public static boolean compile(Path source, Path destination, String... options)
+        throws IOException
+    {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
+
+        List<Path> sources
+            = Files.find(source, Integer.MAX_VALUE,
+                         (file, attrs) -> (file.toString().endsWith(".java")))
+                   .collect(Collectors.toList());
+
+        Files.createDirectories(destination);
+        jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
+                                 Arrays.asList(destination));
+
+        List<String> opts = Arrays.asList(options);
+        JavaCompiler.CompilationTask task
+            = compiler.getTask(null, jfm, null, opts, null,
+                               jfm.getJavaFileObjectsFromPaths(sources));
+
+        return task.call();
+    }
+
+    /**
+     * Compile the specified module from the given module sourcepath
+     *
+     * All warnings/errors emitted by the compiler are output to System.out/err.
+     *
+     * @return true if the compilation is successful
+     *
+     * @throws IOException if there is an I/O error scanning the source tree or
+     *                     creating the destination directory
+     */
+    public static boolean compileModule(Path source, Path destination,
+                                        String moduleName, String... options) {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
+
+        try {
+            Files.createDirectories(destination);
+            jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
+                                     Arrays.asList(destination));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+
+        Stream<String> opts = Arrays.stream(new String[] {
+            "-modulesourcepath", source.toString(), "-m", moduleName
+        });
+        List<String> javacOpts = Stream.concat(opts, Arrays.stream(options))
+                                        .collect(Collectors.toList());
+        JavaCompiler.CompilationTask task
+            = compiler.getTask(null, jfm, null, javacOpts, null, null);
+        return task.call();
+    }
+
+
+    public static void cleanDir(Path dir) throws IOException {
+        if (Files.notExists(dir)) return;
+
+        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                    throws IOException
+            {
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+            }
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException e)
+                    throws IOException
+            {
+                if (e == null) {
+                    Files.delete(dir);
+                    return FileVisitResult.CONTINUE;
+                } else {
+                    // directory iteration failed
+                    throw e;
+                }
+            }
+        });
+        Files.deleteIfExists(dir);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/lib/JdepsUtil.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+import com.sun.tools.jdeps.Analyzer;
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.JdepsConfiguration;
+import com.sun.tools.jdeps.JdepsFilter;
+import com.sun.tools.jdeps.JdepsWriter;
+import com.sun.tools.jdeps.ModuleAnalyzer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Utilities to run jdeps command
+ */
+public final class JdepsUtil {
+    /*
+     * Runs jdeps with the given arguments
+     */
+    public static String[] jdeps(String... args) {
+        String lineSep =     System.getProperty("line.separator");
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        System.err.println("jdeps " + Arrays.stream(args).collect(Collectors.joining(" ")));
+        int rc = com.sun.tools.jdeps.Main.run(args, pw);
+        pw.close();
+        String out = sw.toString();
+        if (!out.isEmpty())
+            System.err.println(out);
+        if (rc != 0)
+            throw new Error("jdeps failed: rc=" + rc);
+        return out.split(lineSep);
+    }
+
+    public static Command newCommand(String cmd) {
+        return new Command(cmd);
+    }
+
+    public static class Command {
+
+        final StringWriter sw = new StringWriter();
+        final PrintWriter pw = new PrintWriter(sw);
+        final JdepsFilter.Builder filter = new JdepsFilter.Builder().filter(true, true);
+        final JdepsConfiguration.Builder builder =  new JdepsConfiguration.Builder();
+        final Set<String> requires = new HashSet<>();
+
+        Analyzer.Type verbose = Analyzer.Type.PACKAGE;
+        boolean apiOnly = false;
+
+        public Command(String cmd) {
+            System.err.println("============ ");
+            System.err.println(cmd);
+        }
+
+        public Command verbose(String verbose) {
+            switch (verbose) {
+                case "-verbose":
+                    this.verbose = Analyzer.Type.VERBOSE;
+                    filter.filter(false, false);
+                    break;
+                case "-verbose:package":
+                    this.verbose = Analyzer.Type.PACKAGE;
+                    break;
+                case "-verbose:class":
+                    this.verbose = Analyzer.Type.CLASS;
+                    break;
+                case "-summary":
+                    this.verbose = Analyzer.Type.SUMMARY;
+                    break;
+                default:
+                    throw new IllegalArgumentException(verbose);
+            }
+            return this;
+        }
+
+        public Command filter(String value) {
+            switch (value) {
+                case "-filter:package":
+                    filter.filter(true, false);
+                    break;
+                case "-filter:archive":
+                case "-filter:module":
+                    filter.filter(false, true);
+                    break;
+                default:
+                    throw new IllegalArgumentException(value);
+            }
+            return this;
+        }
+
+        public Command addClassPath(String classpath) {
+            builder.addClassPath(classpath);
+            return this;
+        }
+
+        public Command addRoot(Path path) {
+            builder.addRoot(path);
+            return this;
+        }
+
+        public Command appModulePath(String modulePath) {
+            builder.appModulePath(modulePath);
+            return this;
+        }
+
+        public Command addmods(Set<String> mods) {
+            builder.addmods(mods);
+            return this;
+        }
+
+        public Command requires(Set<String> mods) {
+            requires.addAll(mods);
+            return this;
+        }
+
+        public Command matchPackages(Set<String> pkgs) {
+            filter.packages(pkgs);
+            return this;
+        }
+
+        public Command regex(String regex) {
+            filter.regex(Pattern.compile(regex));
+            return this;
+        }
+
+        public Command include(String regex) {
+            filter.includePattern(Pattern.compile(regex));
+            return this;
+        }
+
+        public Command includeSystemMoudles(String regex) {
+            filter.includeSystemModules(Pattern.compile(regex));
+            return this;
+        }
+
+        public Command apiOnly() {
+            this.apiOnly = true;
+            return this;
+        }
+
+        public JdepsConfiguration configuration() throws IOException {
+            JdepsConfiguration config = builder.build();
+            requires.forEach(name -> {
+                ModuleDescriptor md = config.findModuleDescriptor(name).get();
+                filter.requires(name, md.packages());
+            });
+            return config;
+        }
+
+        private JdepsWriter writer() {
+            return JdepsWriter.newSimpleWriter(pw, verbose);
+        }
+
+        public DepsAnalyzer getDepsAnalyzer() throws IOException {
+            return new DepsAnalyzer(configuration(), filter.build(), writer(),
+                                    verbose, apiOnly);
+        }
+
+        public ModuleAnalyzer getModuleAnalyzer(Set<String> mods) throws IOException {
+            // if -check is set, add to the root set and all modules are observable
+            addmods(mods);
+            builder.allModules();
+            return new ModuleAnalyzer(configuration(), pw, mods);
+        }
+
+        public void dumpOutput(PrintStream out) {
+            out.println(sw.toString());
+        }
+    }
+
+    /**
+     * Create a jar file using the list of files provided.
+     */
+    public static void createJar(Path jarfile, Path root, Stream<Path> files)
+        throws IOException {
+        Path dir = jarfile.getParent();
+        if (dir != null && Files.notExists(dir)) {
+            Files.createDirectories(dir);
+        }
+        try (JarOutputStream target = new JarOutputStream(
+            Files.newOutputStream(jarfile))) {
+            files.forEach(file -> add(root.relativize(file), file, target));
+        }
+    }
+
+    private static void add(Path path, Path source, JarOutputStream target) {
+        try {
+            String name = path.toString().replace(File.separatorChar, '/');
+            JarEntry entry = new JarEntry(name);
+            entry.setTime(source.toFile().lastModified());
+            target.putNextEntry(entry);
+            Files.copy(source, target);
+            target.closeEntry();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}
--- a/langtools/test/tools/jdeps/m/Foo.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/m/Foo.java	Thu May 19 10:55:33 2016 -0700
@@ -30,4 +30,3 @@
       setF(new f.F());
    }
 }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/CheckModuleTest.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests split packages
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil
+ * @modules jdk.jdeps/com.sun.tools.jdeps
+ * @run testng CheckModuleTest
+ */
+
+import java.lang.module.ModuleDescriptor;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Set;
+
+import com.sun.tools.jdeps.ModuleAnalyzer;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+
+
+public class CheckModuleTest {
+    private static final String TEST_SRC = System.getProperty("test.src");
+    private static final String TEST_CLASSES = System.getProperty("test.classes");
+
+    private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+    private static final Path MODS_DIR = Paths.get("mods");
+
+    // m4 and m5 are analyzed.  Others are compiled to make sure they are present
+    // on the module path for analysis
+    private static final Set<String> modules = Set.of("unsafe", "m4", "m5", "m6", "m7", "m8");
+
+    private static final String JAVA_BASE = "java.base";
+
+    /**
+     * Compiles classes used by the test
+     */
+    @BeforeTest
+    public void compileAll() throws Exception {
+        CompilerUtils.cleanDir(MODS_DIR);
+        modules.forEach(mn ->
+            assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn)));
+    }
+
+    @DataProvider(name = "javaBase")
+    public Object[][] base() {
+        return new Object[][] {
+            { JAVA_BASE, new ModuleMetaData(JAVA_BASE)
+            },
+        };
+    };
+
+    @Test(dataProvider = "javaBase")
+    public void testJavaBase(String name, ModuleMetaData data) throws Exception {
+        JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -check %s -mp %s%n", name, MODS_DIR)
+        );
+        jdeps.appModulePath(MODS_DIR.toString());
+
+        ModuleAnalyzer analyzer = jdeps.getModuleAnalyzer(Set.of(name));
+        assertTrue(analyzer.run());
+        jdeps.dumpOutput(System.err);
+
+        ModuleDescriptor[] descriptors = analyzer.descriptors(name);
+        for (int i=0; i < 3; i++) {
+            descriptors[i].requires().stream()
+                .forEach(req -> data.checkRequires(req));
+        }
+    }
+
+    @DataProvider(name = "modules")
+    public Object[][] unnamed() {
+        return new Object[][]{
+            { "m4", new ModuleMetaData[] {
+                        // original
+                        new ModuleMetaData("m4")
+                            .requiresPublic("java.compiler")
+                            .requires("java.logging")
+                            // unnused exports
+                            .exports("p4.internal", Set.of("m6", "m7")),
+                        // suggested version
+                        new ModuleMetaData("m4")
+                            .requires("java.compiler"),
+                        // reduced version
+                        new ModuleMetaData("m4")
+                            .requires("java.compiler")
+                    }
+            },
+            { "m5", new ModuleMetaData[] {
+                        // original
+                        new ModuleMetaData("m5")
+                            .requiresPublic("java.compiler")
+                            .requiresPublic("java.logging")
+                            .requires("java.sql")
+                            .requiresPublic("m4"),
+                        // suggested version
+                        new ModuleMetaData("m5")
+                            .requiresPublic("java.compiler")
+                            .requires("java.logging")
+                            .requiresPublic("java.sql")
+                            .requiresPublic("m4"),
+                        // reduced version
+                        new ModuleMetaData("m5")
+                            .requiresPublic("java.compiler")
+                            .requiresPublic("java.sql")
+                            .requiresPublic("m4"),
+                    }
+            },
+        };
+    }
+
+    @Test(dataProvider = "modules")
+    public void modularTest(String name, ModuleMetaData[] data) throws Exception {
+        JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -check %s -mp %s%n", name, MODS_DIR)
+        );
+        jdeps.appModulePath(MODS_DIR.toString());
+
+        ModuleAnalyzer analyzer = jdeps.getModuleAnalyzer(Set.of(name));
+        assertTrue(analyzer.run());
+        jdeps.dumpOutput(System.err);
+
+        // compare the module descriptors and the suggested versions
+        ModuleDescriptor[] descriptors = analyzer.descriptors(name);
+        for (int i=0; i < 3; i++) {
+            ModuleMetaData metaData = data[i];
+            descriptors[i].requires().stream()
+                .forEach(req -> metaData.checkRequires(req));
+        }
+
+        Map<String, Set<String>> unused = analyzer.unusedQualifiedExports(name);
+        // verify unuused qualified exports
+        assertEquals(unused, data[0].exports);
+    }
+
+}
--- a/langtools/test/tools/jdeps/modules/GenModuleInfo.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/GenModuleInfo.java	Thu May 19 10:55:33 2016 -0700
@@ -24,8 +24,8 @@
 /*
  * @test
  * @summary Tests jdeps -genmoduleinfo option
- * @library ..
- * @build CompilerUtils
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil
  * @modules jdk.jdeps/com.sun.tools.jdeps
  * @run testng GenModuleInfo
  */
@@ -39,16 +39,12 @@
 
 import java.util.Arrays;
 import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import org.testng.annotations.DataProvider;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
-
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
@@ -86,7 +82,7 @@
 
         for (String mn : modules) {
             Path root = MODS_DIR.resolve(mn);
-            createJar(LIBS_DIR.resolve(mn + ".jar"), root,
+            JdepsUtil.createJar(LIBS_DIR.resolve(mn + ".jar"), root,
                       Files.walk(root, Integer.MAX_VALUE)
                            .filter(f -> {
                                 String fn = f.getFileName().toString();
@@ -100,7 +96,7 @@
         Stream<String> files = Arrays.stream(modules)
                 .map(mn -> LIBS_DIR.resolve(mn + ".jar"))
                 .map(Path::toString);
-        jdeps(Stream.concat(Stream.of("-cp"), files).toArray(String[]::new));
+        JdepsUtil.jdeps(Stream.concat(Stream.of("-cp"), files).toArray(String[]::new));
     }
 
     @Test
@@ -109,7 +105,7 @@
                 .map(mn -> LIBS_DIR.resolve(mn + ".jar"))
                 .map(Path::toString);
 
-        jdeps(Stream.concat(Stream.of("-genmoduleinfo", DEST_DIR.toString()),
+        JdepsUtil.jdeps(Stream.concat(Stream.of("-genmoduleinfo", DEST_DIR.toString()),
                             files)
                     .toArray(String[]::new));
 
@@ -148,7 +144,7 @@
             try (InputStream in1 = Files.newInputStream(p1);
                  InputStream in2 = Files.newInputStream(p2)) {
                 verify(ModuleDescriptor.read(in1),
-                        ModuleDescriptor.read(in2, () -> packages(MODS_DIR.resolve(mn))));
+                       ModuleDescriptor.read(in2, () -> packages(MODS_DIR.resolve(mn))));
             }
         }
     }
@@ -188,46 +184,4 @@
         }
     }
 
-    /*
-     * Runs jdeps with the given arguments
-     */
-    public static String[] jdeps(String... args) {
-        String lineSep =     System.getProperty("line.separator");
-        StringWriter sw = new StringWriter();
-        PrintWriter pw = new PrintWriter(sw);
-        System.err.println("jdeps " + Arrays.toString(args));
-        int rc = com.sun.tools.jdeps.Main.run(args, pw);
-        pw.close();
-        String out = sw.toString();
-        if (!out.isEmpty())
-            System.err.println(out);
-        if (rc != 0)
-            throw new Error("jdeps failed: rc=" + rc);
-        return out.split(lineSep);
-    }
-
-    /**
-     * Create a jar file using the list of files provided.
-     */
-    public static void createJar(Path jarfile, Path root, Stream<Path> files)
-            throws IOException {
-        try (JarOutputStream target = new JarOutputStream(
-                Files.newOutputStream(jarfile))) {
-           files.forEach(file -> add(root.relativize(file), file, target));
-        }
-    }
-
-    private static void add(Path path, Path source, JarOutputStream target) {
-        try {
-            String name = path.toString().replace(File.separatorChar, '/');
-            JarEntry entry = new JarEntry(name);
-            entry.setTime(source.toFile().lastModified());
-            target.putNextEntry(entry);
-            Files.copy(source, target);
-            target.closeEntry();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/ModuleMetaData.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static com.sun.tools.jdeps.DepsAnalyzer.Info.*;
+import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
+import static java.lang.module.ModuleDescriptor.*;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+
+public class ModuleMetaData {
+    public static final String JAVA_BASE = "java.base";
+
+    static final String INTERNAL = "(internal)";
+    static final String QUALIFIED = "(qualified)";
+    static final String JDK_INTERNAL = "JDK internal API";
+
+    final String moduleName;
+    final boolean isNamed;
+    final Map<String, ModuleRequires> requires = new LinkedHashMap<>();
+    final Map<String, Dependence> references = new LinkedHashMap<>();
+    final Map<String, Set<String>> exports = new LinkedHashMap<>();
+
+    ModuleMetaData(String name) {
+        this(name, true);
+    }
+
+    ModuleMetaData(String name, boolean isNamed) {
+        this.moduleName = name;
+        this.isNamed = isNamed;
+        requires(JAVA_BASE);  // implicit requires
+    }
+
+    String name() {
+        return moduleName;
+    }
+
+    ModuleMetaData requires(String name) {
+        requires.put(name, new ModuleRequires(name));
+        return this;
+    }
+
+    ModuleMetaData requiresPublic(String name) {
+        requires.put(name, new ModuleRequires(name, PUBLIC));
+        return this;
+    }
+
+    // for unnamed module
+    ModuleMetaData depends(String name) {
+        requires.put(name, new ModuleRequires(name));
+        return this;
+    }
+
+    ModuleMetaData reference(String origin, String target, String module) {
+        return dependence(origin, target, module, "");
+    }
+
+    ModuleMetaData internal(String origin, String target, String module) {
+        return dependence(origin, target, module, INTERNAL);
+    }
+
+    ModuleMetaData qualified(String origin, String target, String module) {
+        return dependence(origin, target, module, QUALIFIED);
+    }
+
+    ModuleMetaData jdkInternal(String origin, String target, String module) {
+        return dependence(origin, target, module, JDK_INTERNAL);
+    }
+
+    ModuleMetaData exports(String pn, Set<String> targets) {
+        exports.put(pn, targets);
+        return this;
+    }
+
+    private ModuleMetaData dependence(String origin, String target, String module, String access) {
+        references.put(key(origin, target), new Dependence(origin, target, module, access));
+        return this;
+    }
+
+    String key(String origin, String target) {
+        return origin + ":" + target;
+    }
+
+    void checkRequires(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
+        // System.err.format("%s: Expected %s Found %s %n", name, requires, adjacentNodes);
+        adjacentNodes.stream()
+            .forEach(v -> checkRequires(v.name));
+        assertEquals(adjacentNodes.size(), requires.size());
+    }
+
+    void checkRequires(String name) {
+        ModuleRequires req = requires.get(name);
+        if (req == null)
+            System.err.println(moduleName + ": unexpected requires " + name);
+        assertTrue(requires.containsKey(name));
+    }
+
+    void checkRequires(Requires require) {
+        String name = require.name();
+        if (name.equals(JAVA_BASE))
+            return;
+
+        ModuleRequires req = requires.get(name);
+        if (req == null)
+            System.err.format("%s: unexpected dependence %s%n", moduleName, name);
+
+        assertTrue(requires.containsKey(name));
+
+        assertEquals(require.modifiers(), req.modifiers());
+    }
+
+    void checkDependences(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
+        // System.err.format("%s: Expected %s Found %s %n", name, references, adjacentNodes);
+
+        adjacentNodes.stream()
+            .forEach(v -> checkDependence(name, v.name, v.source, v.info));
+        assertEquals(adjacentNodes.size(), references.size());
+    }
+
+    void checkDependence(String origin, String target, String module, DepsAnalyzer.Info info) {
+        String key = key(origin, target);
+        Dependence dep = references.get(key);
+        String access = "";
+        if (info == QUALIFIED_EXPORTED_API)
+            access = QUALIFIED;
+        else if (info == JDK_INTERNAL_API)
+            access = JDK_INTERNAL;
+        else if (info == INTERNAL_API)
+            access = INTERNAL;
+
+        assertTrue(references.containsKey(key));
+
+        assertEquals(dep.access, access);
+        assertEquals(dep.module, module);
+    }
+
+
+    public static class ModuleRequires {
+        final String name;
+        final Requires.Modifier mod;
+
+        ModuleRequires(String name) {
+            this.name = name;
+            this.mod = null;
+        }
+
+        ModuleRequires(String name, Requires.Modifier mod) {
+            this.name = name;
+            this.mod = mod;
+        }
+
+        Set<Requires.Modifier> modifiers() {
+            return mod != null ? Set.of(mod) : Collections.emptySet();
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+    public static class Dependence {
+        final String origin;
+        final String target;
+        final String module;
+        final String access;
+
+        Dependence(String origin, String target, String module, String access) {
+            this.origin = origin;
+            this.target = target;
+            this.module = module;
+            this.access = access;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s -> %s (%s) %s", origin, target, module, access);
+        }
+    }
+}
--- a/langtools/test/tools/jdeps/modules/ModuleTest.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/ModuleTest.java	Thu May 19 10:55:33 2016 -0700
@@ -24,30 +24,28 @@
 /*
  * @test
  * @summary Tests jdeps -m and -mp options on named modules and unnamed modules
- * @library ..
- * @build CompilerUtils
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil
  * @modules jdk.jdeps/com.sun.tools.jdeps
  * @run testng ModuleTest
  */
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleDescriptor.Requires.Modifier;
-import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
+import java.io.File;
+import java.io.IOException;
 
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.*;
-
 import java.util.stream.Collectors;
 
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.Graph;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
 public class ModuleTest {
@@ -56,6 +54,7 @@
 
     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
     private static final Path MODS_DIR = Paths.get("mods");
+    private static final Path UNNAMED_DIR = Paths.get("unnamed");
 
     // the names of the modules in this test
     private static final String UNSUPPORTED = "unsupported";
@@ -66,62 +65,68 @@
     @BeforeTest
     public void compileAll() throws Exception {
         CompilerUtils.cleanDir(MODS_DIR);
+        CompilerUtils.cleanDir(UNNAMED_DIR);
+
         assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, UNSUPPORTED,
                                                "-XaddExports:java.base/jdk.internal.perf=" + UNSUPPORTED));
         // m4 is not referenced
         Arrays.asList("m1", "m2", "m3", "m4")
               .forEach(mn -> assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn)));
+
+        assertTrue(CompilerUtils.compile(SRC_DIR.resolve("m3"), UNNAMED_DIR, "-mp", MODS_DIR.toString()));
+        Files.delete(UNNAMED_DIR.resolve("module-info.class"));
     }
 
     @DataProvider(name = "modules")
     public Object[][] expected() {
         return new Object[][]{
-                { "m3", new Data("m3").requiresPublic("java.sql")
-                                      .requiresPublic("m2")
-                                      .requires("java.logging")
-                                      .requiresPublic("m1")
-                                      .reference("p3", "java.lang", "java.base")
-                                      .reference("p3", "java.sql", "java.sql")
-                                      .reference("p3", "java.util.logging", "java.logging")
-                                      .reference("p3", "p1", "m1")
-                                      .reference("p3", "p2", "m2")
-                                      .qualified("p3", "p2.internal", "m2")
+                { "m3", new ModuleMetaData("m3").requiresPublic("java.sql")
+                            .requiresPublic("m2")
+                            .requires("java.logging")
+                            .requiresPublic("m1")
+                            .reference("p3", "java.lang", "java.base")
+                            .reference("p3", "java.sql", "java.sql")
+                            .reference("p3", "java.util.logging", "java.logging")
+                            .reference("p3", "p1", "m1")
+                            .reference("p3", "p2", "m2")
+                            .qualified("p3", "p2.internal", "m2")
                 },
-                { "m2", new Data("m2").requiresPublic("m1")
-                                      .reference("p2", "java.lang", "java.base")
-                                      .reference("p2", "p1", "m1")
-                                      .reference("p2.internal", "java.lang", "java.base")
-                                      .reference("p2.internal", "java.io", "java.base")
+                { "m2", new ModuleMetaData("m2").requiresPublic("m1")
+                            .reference("p2", "java.lang", "java.base")
+                            .reference("p2", "p1", "m1")
+                            .reference("p2.internal", "java.lang", "java.base")
+                            .reference("p2.internal", "java.io", "java.base")
                 },
-                { "m1", new Data("m1").requires("unsupported")
-                                      .reference("p1", "java.lang", "java.base")
-                                      .reference("p1.internal", "java.lang", "java.base")
-                                      .reference("p1.internal", "p1", "m1")
-                                      .reference("p1.internal", "q", "unsupported")
+                { "m1", new ModuleMetaData("m1").requires("unsupported")
+                            .reference("p1", "java.lang", "java.base")
+                            .reference("p1.internal", "java.lang", "java.base")
+                            .reference("p1.internal", "p1", "m1")
+                            .reference("p1.internal", "q", "unsupported")
                 },
-                { "unsupported", new Data("unsupported")
-                                      .reference("q", "java.lang", "java.base")
-                                      .jdkInternal("q", "jdk.internal.perf", "(java.base)")
+                { "unsupported", new ModuleMetaData("unsupported")
+                            .reference("q", "java.lang", "java.base")
+                            .jdkInternal("q", "jdk.internal.perf", "java.base")
                 },
         };
     }
 
     @Test(dataProvider = "modules")
-    public void modularTest(String name, Data data) {
-        // print only the specified module
-        String excludes = Arrays.stream(modules)
-                                .filter(mn -> !mn.endsWith(name))
-                                .collect(Collectors.joining(","));
-        String[] result = jdeps("-exclude-modules", excludes,
-                                "-mp", MODS_DIR.toString(),
-                                "-m", name);
-        assertTrue(data.check(result));
+    public void modularTest(String name, ModuleMetaData data) throws IOException {
+        // jdeps -modulepath mods -m <name>
+        runTest(data, MODS_DIR.toString(), Set.of(name));
+
+        // jdeps -modulepath libs/m1.jar:.... -m <name>
+        String mp = Arrays.stream(modules)
+                .filter(mn -> !mn.equals(name))
+                .map(mn -> MODS_DIR.resolve(mn).toString())
+                .collect(Collectors.joining(File.pathSeparator));
+        runTest(data, mp, Collections.emptySet(), MODS_DIR.resolve(name));
     }
 
     @DataProvider(name = "unnamed")
     public Object[][] unnamed() {
         return new Object[][]{
-                { "m3", new Data("m3", false)
+                { "unnamed", new ModuleMetaData("unnamed", false)
                             .depends("java.sql")
                             .depends("java.logging")
                             .depends("m1")
@@ -133,178 +138,43 @@
                             .reference("p3", "p2", "m2")
                             .internal("p3", "p2.internal", "m2")
                 },
-                { "unsupported", new Data("unsupported", false)
-                            .reference("q", "java.lang", "java.base")
-                            .jdkInternal("q", "jdk.internal.perf", "(java.base)")
-                },
         };
     }
 
     @Test(dataProvider = "unnamed")
-    public void unnamedTest(String name, Data data) {
-        String[] result = jdeps("-mp", MODS_DIR.toString(), MODS_DIR.resolve(name).toString());
-        assertTrue(data.check(result));
-    }
-
-    /*
-     * Runs jdeps with the given arguments
-     */
-    public static String[] jdeps(String... args) {
-        String lineSep =     System.getProperty("line.separator");
-        StringWriter sw = new StringWriter();
-        PrintWriter pw = new PrintWriter(sw);
-        System.err.println("jdeps " + Arrays.toString(args));
-        int rc = com.sun.tools.jdeps.Main.run(args, pw);
-        pw.close();
-        String out = sw.toString();
-        if (!out.isEmpty())
-            System.err.println(out);
-        if (rc != 0)
-            throw new Error("jdeps failed: rc=" + rc);
-        return out.split(lineSep);
+    public void unnamedTest(String name, ModuleMetaData data) throws IOException {
+        runTest(data, MODS_DIR.toString(), Set.of("m1", "m2"), UNNAMED_DIR);
     }
 
-    static class Data {
-        static final String INTERNAL = "(internal)";
-        static final String QUALIFIED = "(qualified)";
-        static final String JDK_INTERNAL = "JDK internal API";
-
-        final String moduleName;
-        final boolean isNamed;
-        final Map<String, ModuleRequires> requires = new LinkedHashMap<>();
-        final Map<String, Dependence> references = new LinkedHashMap<>();
-        Data(String name) {
-            this(name, true);
-        }
-        Data(String name, boolean isNamed) {
-            this.moduleName = name;
-            this.isNamed = isNamed;
-            requires("java.base");  // implicit requires
-        }
+    private void runTest(ModuleMetaData data, String modulepath,
+                         Set<String> roots, Path... paths)
+        throws IOException
+    {
+        // jdeps -modulepath <modulepath> -m root paths
 
-        Data requires(String name) {
-            requires.put(name, new ModuleRequires(name));
-            return this;
-        }
-        Data requiresPublic(String name) {
-            requires.put(name, new ModuleRequires(name, PUBLIC));
-            return this;
-        }
-        // for unnamed module
-        Data depends(String name) {
-            requires.put(name, new ModuleRequires(name));
-            return this;
-        }
-        Data reference(String origin, String target, String module) {
-            return dependence(origin, target, module, "");
-        }
-        Data internal(String origin, String target, String module) {
-            return dependence(origin, target, module, INTERNAL);
-        }
-        Data qualified(String origin, String target, String module) {
-            return dependence(origin, target, module, QUALIFIED);
-        }
-        Data jdkInternal(String origin, String target, String module) {
-            return dependence(origin, target, module, JDK_INTERNAL);
-        }
-        private Data dependence(String origin, String target, String module, String access) {
-            references.put(key(origin, target), new Dependence(origin, target, module, access));
-            return this;
-        }
+        JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -modulepath %s -addmods %s %s%n", MODS_DIR,
+                          roots.stream().collect(Collectors.joining(",")), paths)
+        );
+        jdeps.appModulePath(modulepath)
+             .addmods(roots);
+        Arrays.stream(paths).forEach(jdeps::addRoot);
 
-        String key(String origin, String target) {
-            return origin+":"+target;
-        }
-        boolean check(String[] lines) {
-            System.out.format("verifying module %s%s%n", moduleName, isNamed ? "" : " (unnamed module)");
-            for (String l : lines) {
-                String[] tokens = l.trim().split("\\s+");
-                System.out.println("  " + Arrays.stream(tokens).collect(Collectors.joining(" ")));
-                switch (tokens[0]) {
-                    case "module":
-                        assertEquals(tokens.length, 2);
-                        assertEquals(moduleName, tokens[1]);
-                        break;
-                    case "requires":
-                        String name = tokens.length == 2 ? tokens[1] : tokens[2];
-                        Modifier modifier = null;
-                        if (tokens.length == 3) {
-                            assertEquals("public", tokens[1]);
-                            modifier = PUBLIC;
-                        }
-                        checkRequires(name, modifier);
-                        break;
-                    default:
-                        if (tokens.length == 3) {
-                            // unnamed module requires
-                            assertFalse(isNamed);
-                            assertEquals(moduleName, tokens[0]);
-                            String mn = tokens[2];
-                            checkRequires(mn, null);
-                        } else {
-                            checkDependence(tokens);
-                        }
-                }
-            }
-            return true;
-        }
-
-        private void checkRequires(String name, Modifier modifier) {
-            assertTrue(requires.containsKey(name));
-            ModuleRequires req = requires.get(name);
-            assertEquals(req.mod, modifier);
-        }
+        // run the analyzer
+        DepsAnalyzer analyzer = jdeps.getDepsAnalyzer();
+        assertTrue(analyzer.run());
 
-        private void checkDependence(String[] tokens) {
-            assertTrue(tokens.length >= 4);
-            String origin = tokens[0];
-            String target = tokens[2];
-            String module = tokens[3];
-            String key = key(origin, target);
-            assertTrue(references.containsKey(key));
-            Dependence dep = references.get(key);
-            if (tokens.length == 4) {
-                assertEquals(dep.access, "");
-            } else if (tokens.length == 5) {
-                assertEquals(dep.access, tokens[4]);
-            } else {
-                // JDK internal API
-                module = tokens[6];
-                assertEquals(tokens.length, 7);
-                assertEquals(tokens[3], "JDK");
-                assertEquals(tokens[4], "internal");
-                assertEquals(tokens[5], "API");
-            }
-            assertEquals(dep.module, module);
-        }
+        // analyze result
+        Graph<DepsAnalyzer.Node> g1 = analyzer.moduleGraph();
+        g1.nodes().stream()
+            .filter(u -> u.name.equals(data.moduleName))
+            .forEach(u -> data.checkRequires(u.name, g1.adjacentNodes(u)));
 
-        public static class ModuleRequires {
-            final String name;
-            final ModuleDescriptor.Requires.Modifier mod;
-
-            ModuleRequires(String name) {
-                this.name = name;
-                this.mod = null;
-            }
+        Graph<DepsAnalyzer.Node> g2 = analyzer.dependenceGraph();
+        g2.nodes().stream()
+            .filter(u -> u.name.equals(data.moduleName))
+            .forEach(u -> data.checkDependences(u.name, g2.adjacentNodes(u)));
 
-            ModuleRequires(String name, ModuleDescriptor.Requires.Modifier mod) {
-                this.name = name;
-                this.mod = mod;
-            }
-        }
-
-        public static class Dependence {
-            final String origin;
-            final String target;
-            final String module;
-            final String access;
-
-            Dependence(String origin, String target, String module, String access) {
-                this.origin = origin;
-                this.target = target;
-                this.module = module;
-                this.access = access;
-            }
-        }
+        jdeps.dumpOutput(System.err);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/SplitPackage.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests split packages
+ * @library ../lib
+ * @build CompilerUtils
+ * @modules jdk.jdeps/com.sun.tools.jdeps
+ * @run testng SplitPackage
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.JdepsConfiguration;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+public class SplitPackage {
+    private static final String TEST_SRC = System.getProperty("test.src");
+
+    private static final Path CLASSES_DIR = Paths.get("classes");
+
+    private static final String SPLIT_PKG_NAME = "javax.annotation";
+    private static final String JAVA_ANNOTATIONS_COMMON = "java.annotations.common";
+    /**
+     * Compiles classes used by the test
+     */
+    @BeforeTest
+    public void compileAll() throws Exception {
+        CompilerUtils.cleanDir(CLASSES_DIR);
+        assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "patches"), CLASSES_DIR));
+    }
+
+    @Test
+    public void runTest() throws Exception {
+        // Test jdeps classes
+        runTest(null);
+        // Test jdeps -addmods
+        runTest(JAVA_ANNOTATIONS_COMMON, SPLIT_PKG_NAME);
+    }
+
+    private void runTest(String root, String... splitPackages) throws Exception {
+        JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -verbose:class -addmods %s %s%n",
+                          root, CLASSES_DIR)
+        );
+        jdeps.verbose("-verbose:class")
+            .addRoot(CLASSES_DIR);
+        if (root != null)
+            jdeps.addmods(Set.of(root));
+
+
+        JdepsConfiguration config = jdeps.configuration();
+        Map<String, Set<String>> pkgs = config.splitPackages();
+
+        final Set<String> expected;
+        if (splitPackages != null) {
+            expected = Arrays.stream(splitPackages).collect(Collectors.toSet());
+        } else {
+            expected = Collections.emptySet();
+        }
+
+        if (!pkgs.keySet().equals(expected)) {
+            throw new RuntimeException(splitPackages.toString());
+        }
+
+        // java.annotations.common is not observable
+        DepsAnalyzer analyzer = jdeps.getDepsAnalyzer();
+
+        assertTrue(analyzer.run());
+
+        jdeps.dumpOutput(System.err);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/TransitiveDeps.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests jdeps -m and -mp options on named modules and unnamed modules
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil
+ * @modules jdk.jdeps/com.sun.tools.jdeps
+ * @run testng TransitiveDeps
+ */
+
+import java.io.File;
+import java.io.IOException;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.Graph;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+public class TransitiveDeps {
+    private static final String TEST_SRC = System.getProperty("test.src");
+
+    private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+    private static final Path MODS_DIR = Paths.get("mods");
+    private static final Path LIBS_DIR = Paths.get("libs");
+
+    // the names of the modules in this test
+    private static String[] modules = new String[] {"unsafe", "m6", "m7"};
+    /**
+     * Compiles all modules used by the test
+     */
+    @BeforeTest
+    public void compileAll() throws Exception {
+        CompilerUtils.cleanDir(MODS_DIR);
+        CompilerUtils.cleanDir(LIBS_DIR);
+
+        for (String mn : modules) {
+            // compile a module
+            assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn));
+
+            // create JAR files with no module-info.class
+            Path root = MODS_DIR.resolve(mn);
+            JdepsUtil.createJar(LIBS_DIR.resolve(mn + ".jar"), root,
+                Files.walk(root, Integer.MAX_VALUE)
+                    .filter(f -> {
+                        String fn = f.getFileName().toString();
+                        return fn.endsWith(".class") && !fn.equals("module-info.class");
+                    }));
+        }
+    }
+
+    @DataProvider(name = "modules")
+    public Object[][] expected1() {
+        return new Object[][]{
+            { "m7",
+               List.of(new ModuleMetaData("m7")
+                            .requires("m6")
+                            .requires("unsafe")
+                            .reference("p7.Main", "java.lang.Object", "java.base")
+                            .reference("p7.Main", "java.lang.String", "java.base")
+                            .reference("p7.Main", "org.safe.Lib", "unsafe")
+                            .reference("p7.Main", "p6.safe.Lib", "m6"),
+                        new ModuleMetaData("m6")
+                            .requires("unsafe")
+                            .reference("p6.indirect.UnsafeRef", "java.lang.Object", "java.base")
+                            .reference("p6.indirect.UnsafeRef", "org.unsafe.UseUnsafe ", "unsafe")
+                            .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
+                            .reference("p6.safe.Lib", "java.lang.Class", "java.base")
+                            .reference("p6.safe.Lib", "java.lang.Object", "java.base")
+                            .reference("p6.safe.Lib", "java.lang.String", "java.base")
+                            .reference("p6.safe.Lib", "java.lang.System", "java.base")
+                            .reference("p6.safe.Lib", "org.safe.Lib", "unsafe"),
+                        new ModuleMetaData("unsafe")
+                            .requires("jdk.unsupported")
+                            .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
+                            .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
+                            .reference("org.safe.Lib", "java.lang.Class", "java.base")
+                            .reference("org.safe.Lib", "java.lang.Object", "java.base")
+                            .reference("org.safe.Lib", "java.lang.String", "java.base")
+                            .reference("org.safe.Lib", "java.lang.System", "java.base")
+                            .reference("org.unsafe.UseUnsafe", "java.lang.Object", "java.base")
+                            .jdkInternal("org.unsafe.UseUnsafe", "sun.misc.Unsafe", "java.base")
+                        )
+            },
+        };
+    }
+
+    @Test(dataProvider = "modules")
+    public void testModulePath(String name, List<ModuleMetaData> data) throws IOException {
+        Set<String> roots = Set.of("m6", "unsafe");
+        JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -modulepath %s -addmods %s -m %s%n", MODS_DIR,
+                          roots.stream().collect(Collectors.joining(",")), name)
+        );
+        jdeps.verbose("-verbose:class")
+             .appModulePath(MODS_DIR.toString())
+             .addmods(roots)
+             .addmods(Set.of(name));
+
+        runJdeps(jdeps, data);
+
+        // run automatic modules
+        roots = Set.of("ALL-MODULE-PATH", "jdk.unsupported");
+
+        jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -modulepath %s -addmods %s -m %s%n", LIBS_DIR,
+                          roots.stream().collect(Collectors.joining(",")), name)
+        );
+        jdeps.verbose("-verbose:class")
+            .appModulePath(LIBS_DIR.toString())
+            .addmods(roots)
+            .addmods(Set.of(name));
+
+        runJdeps(jdeps, data);
+    }
+
+    @DataProvider(name = "jars")
+    public Object[][] expected2() {
+        return new Object[][]{
+            { "m7", List.of(new ModuleMetaData("m7.jar")
+                                .requires("m6.jar")
+                                .requires("unsafe.jar")
+                                .reference("p7.Main", "java.lang.Object", "java.base")
+                                .reference("p7.Main", "java.lang.String", "java.base")
+                                .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
+                                .reference("p7.Main", "p6.safe.Lib", "m6.jar"))
+            },
+        };
+    }
+
+    @Test(dataProvider = "jars")
+    public void testClassPath(String name, List<ModuleMetaData> data) throws IOException {
+        String cpath = Arrays.stream(modules)
+            .filter(mn -> !mn.equals(name))
+            .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
+            .collect(Collectors.joining(File.pathSeparator));
+
+        Path jarfile = LIBS_DIR.resolve(name + ".jar");
+        JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -classpath %s %s%n", cpath, jarfile)
+        );
+        jdeps.verbose("-verbose:class")
+             .addClassPath(cpath)
+             .addRoot(jarfile);
+
+        runJdeps(jdeps, data);
+    }
+
+    @DataProvider(name = "compileTimeView")
+    public Object[][] expected3() {
+        return new Object[][] {
+            {"m7",
+             List.of(new ModuleMetaData("m7.jar")
+                        .requires("m6.jar")
+                        .requires("unsafe.jar")
+                        .reference("p7.Main", "java.lang.Object", "java.base")
+                        .reference("p7.Main", "java.lang.String", "java.base")
+                        .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
+                        .reference("p7.Main", "p6.safe.Lib", "m6.jar"),
+                    new ModuleMetaData("m6.jar")
+                        .requires("unsafe.jar")
+                        .reference("p6.indirect.UnsafeRef", "java.lang.Object", "java.base")
+                        .reference("p6.indirect.UnsafeRef", "org.unsafe.UseUnsafe ", "unsafe.jar")
+                        .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
+                        .reference("p6.safe.Lib", "java.lang.Class", "java.base")
+                        .reference("p6.safe.Lib", "java.lang.Object", "java.base")
+                        .reference("p6.safe.Lib", "java.lang.String", "java.base")
+                        .reference("p6.safe.Lib", "java.lang.System", "java.base")
+                        .reference("p6.safe.Lib", "org.safe.Lib", "unsafe.jar"),
+                    new ModuleMetaData("unsafe.jar")
+                        .requires("jdk.unsupported")
+                        .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
+                        .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
+                        .reference("org.safe.Lib", "java.lang.Class", "java.base")
+                        .reference("org.safe.Lib", "java.lang.Object", "java.base")
+                        .reference("org.safe.Lib", "java.lang.String", "java.base")
+                        .reference("org.safe.Lib", "java.lang.System", "java.base")
+                        .reference("org.unsafe.UseUnsafe", "java.lang.Object", "java.base")
+                        .jdkInternal("org.unsafe.UseUnsafe", "sun.misc.Unsafe", "java.base")
+                )
+            },
+        };
+    }
+
+    @Test(dataProvider = "compileTimeView")
+    public void compileTimeView(String name, List<ModuleMetaData> data) throws IOException {
+        String cpath = Arrays.stream(modules)
+                .filter(mn -> !mn.equals(name))
+                .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
+                .collect(Collectors.joining(File.pathSeparator));
+
+        Path jarfile = LIBS_DIR.resolve(name + ".jar");
+
+        JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -ct -classpath %s %s%n", cpath, jarfile)
+        );
+
+        jdeps.verbose("-verbose:class")
+             .addClassPath(cpath)
+             .addRoot(jarfile);
+
+        runJdeps(jdeps, data, true, 0 /* -recursive */);
+    }
+
+    @DataProvider(name = "recursiveDeps")
+    public Object[][] expected4() {
+        return new Object[][] {
+            {"m7",
+                List.of(new ModuleMetaData("m7.jar")
+                        .requires("m6.jar")
+                        .requires("unsafe.jar")
+                        .reference("p7.Main", "java.lang.Object", "java.base")
+                        .reference("p7.Main", "java.lang.String", "java.base")
+                        .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
+                        .reference("p7.Main", "p6.safe.Lib", "m6.jar"),
+                    new ModuleMetaData("m6.jar")
+                        .requires("unsafe.jar")
+                        .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
+                        .reference("p6.safe.Lib", "java.lang.Class", "java.base")
+                        .reference("p6.safe.Lib", "java.lang.Object", "java.base")
+                        .reference("p6.safe.Lib", "java.lang.String", "java.base")
+                        .reference("p6.safe.Lib", "java.lang.System", "java.base")
+                        .reference("p6.safe.Lib", "org.safe.Lib", "unsafe.jar"),
+                    new ModuleMetaData("unsafe.jar")
+                        .requires("jdk.unsupported")
+                        .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
+                        .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
+                        .reference("org.safe.Lib", "java.lang.Class", "java.base")
+                        .reference("org.safe.Lib", "java.lang.Object", "java.base")
+                        .reference("org.safe.Lib", "java.lang.String", "java.base")
+                        .reference("org.safe.Lib", "java.lang.System", "java.base")
+                )
+            },
+        };
+    }
+    @Test(dataProvider = "recursiveDeps")
+    public void recursiveDeps(String name, List<ModuleMetaData> data) throws IOException {
+        String cpath = Arrays.stream(modules)
+            .filter(mn -> !mn.equals(name))
+            .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
+            .collect(Collectors.joining(File.pathSeparator));
+
+        Path jarfile = LIBS_DIR.resolve(name + ".jar");
+
+        JdepsUtil.Command jdeps = JdepsUtil.newCommand(
+            String.format("jdeps -R -classpath %s %s%n", cpath, jarfile)
+        );
+        jdeps.verbose("-verbose:class").filter("-filter:archive")
+             .addClassPath(cpath)
+             .addRoot(jarfile);
+
+        runJdeps(jdeps, data, true, 0 /* -recursive */);
+    }
+
+    private void runJdeps(JdepsUtil.Command jdeps, List<ModuleMetaData> data)
+        throws IOException
+    {
+        runJdeps(jdeps, data, false, 1 /* depth */);
+    }
+
+    private void runJdeps(JdepsUtil.Command jdeps, List<ModuleMetaData> data,
+                          boolean compileTimeView, int depth)
+        throws IOException
+    {
+        // run the analyzer
+        DepsAnalyzer analyzer = jdeps.getDepsAnalyzer();
+        assertTrue(analyzer.run(compileTimeView, depth));
+        jdeps.dumpOutput(System.err);
+
+        // analyze result
+        Graph<DepsAnalyzer.Node> g1 = analyzer.moduleGraph();
+        Map<String, ModuleMetaData> dataMap = data.stream()
+            .collect(Collectors.toMap(ModuleMetaData::name, Function.identity()));
+
+        // the returned graph contains all nodes such as java.base and jdk.unsupported
+        g1.nodes().stream()
+            .filter(u -> dataMap.containsKey(u.name))
+            .forEach(u -> {
+                ModuleMetaData md = dataMap.get(u.name);
+                md.checkRequires(u.name, g1.adjacentNodes(u));
+            });
+
+        Graph<DepsAnalyzer.Node> g2 = analyzer.dependenceGraph();
+
+        g2.nodes().stream()
+            .filter(u -> dataMap.containsKey(u.name))
+            .forEach(u -> {
+                ModuleMetaData md = dataMap.get(u.name);
+                md.checkDependences(u.name, g2.adjacentNodes(u));
+            });
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/patches/javax/annotation/NonNull.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 javax.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+
+@Documented
+@Target({FIELD, LOCAL_VARIABLE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NonNull {
+}
--- a/langtools/test/tools/jdeps/modules/src/m4/module-info.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/src/m4/module-info.java	Thu May 19 10:55:33 2016 -0700
@@ -22,8 +22,14 @@
  */
 
 module m4 {
+    // not used in signature
     requires public java.compiler;
+
+    // unused dependence
     requires java.logging;
+
     exports p4;
-    exports p4.internal to m1,m2,m3;
+
+    // unuused qualified exports
+    exports p4.internal to m6,m7;
 }
--- a/langtools/test/tools/jdeps/modules/src/m4/p4/internal/Impl.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/src/m4/p4/internal/Impl.java	Thu May 19 10:55:33 2016 -0700
@@ -23,9 +23,14 @@
 
 package p4.internal;
 
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
 
 public class Impl {
+    private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+
     public String name() {
         return Impl.class.getName();
     }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m5/module-info.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+module m5 {
+    // m4 requires public java.compilerr
+    requires public m4;
+    requires public java.compiler;
+
+    // java.sql should be requires public
+    requires java.sql;
+
+    // java.logging is used for implementation only
+    requires public java.logging;
+
+    exports p5;
+
+    // m8 is not in the resolved graph but used by m8
+    exports p5.internal to m8;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m5/p5/Main.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 p5;
+
+import java.sql.Driver;
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+
+public class Main {
+    public void run(Driver driver) throws Exception {
+        driver.getParentLogger().config("test");
+
+    }
+
+    public p4.Lib getLib() {
+        return new p4.Lib();
+    }
+
+    public JavaCompiler getCompiler() {
+        return ToolProvider.getSystemJavaCompiler();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m5/p5/internal/T.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 p5.internal;
+
+public class T {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m6/module-info.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+module m6 {
+    requires unsafe;
+
+    // no dependency on sun.misc.Unsafe directly or indirectly
+    exports p6.safe;
+
+    // direct dependency on org.unsafe
+    // hence indirect dependency on sun.misc.Unsafe
+    exports p6.indirect;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m6/p6/indirect/UnsafeRef.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 p6.indirect;
+
+// indirectly depend on sun.misc.Unsafe
+public class UnsafeRef {
+     public static org.unsafe.UseUnsafe get() {
+         return new org.unsafe.UseUnsafe();
+     }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m6/p6/safe/Lib.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 p6.safe;
+
+// no direct or indirect dependency on sun.misc.Unsafe
+public class Lib {
+    public static void doit() {
+        System.out.println(Lib.class.getName());
+        org.safe.Lib.doit();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m7/module-info.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+module m7 {
+    // only use classes that have no direct or indirect dependency
+    // to sun.misc.Unsafe
+    requires unsafe;
+    requires m6;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m7/p7/Main.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 p7;
+
+// Only use classes in unsafe and m6 modules with no
+// direct or indirect dependency on sun.misc.Unsafe
+public class Main {
+    public static void main(String... args) {
+        p6.safe.Lib.doit();
+        org.safe.Lib.doit();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m8/module-info.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+module m8 {
+    requires m5;
+
+    // use p5.internal
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/m8/p8/Main.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 p8;
+
+import p5.internal.T;
+
+public class Main {
+    public static void main() {
+        T t = new T();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/unsafe/module-info.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+module unsafe {
+    requires jdk.unsupported;
+
+    // direct dependency on sun.misc.Unsafe
+    exports org.unsafe;
+
+    // no dependency on sun.misc.Unsafe directly or indirectly
+    exports org.safe;
+
+    // indirect dependency on sun.misc.Unsafe
+    exports org.indirect;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/unsafe/org/indirect/UnsafeRef.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 org.indirect;
+
+// indirectly depend on sun.misc.Unsafe
+public class UnsafeRef {
+     public static org.unsafe.UseUnsafe get() {
+         return new org.unsafe.UseUnsafe();
+     }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/unsafe/org/safe/Lib.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 org.safe;
+
+// no direct or indirect dependency on sun.misc.Unsafe
+public class Lib {
+    public static void doit() {
+        System.out.println(Lib.class.getName());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/modules/src/unsafe/org/unsafe/UseUnsafe.java	Thu May 19 10:55:33 2016 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 org.unsafe;
+
+import sun.misc.Unsafe;
+
+public class UseUnsafe {
+    static Unsafe unsafe = Unsafe.getUnsafe();
+}
--- a/langtools/test/tools/jdeps/unsupported/JDKUnsupportedTest.java	Thu May 19 10:55:33 2016 -0700
+++ b/langtools/test/tools/jdeps/unsupported/JDKUnsupportedTest.java	Thu May 19 10:55:33 2016 -0700
@@ -62,7 +62,7 @@
     public void test(String filename, String[][] expected) {
         Path path = Paths.get(TEST_CLASSES, filename);
 
-        Map<String, String> result = jdeps("-M", path.toString());
+        Map<String, String> result = jdeps(path.toString());
         for (String[] e : expected) {
             String pn = e[0];
             String module = e[1];