8029548: (jdeps) use @jdk.Exported to determine supported vs JDK internal API

Thu, 17 Jul 2014 15:23:08 -0700

author
mchung
date
Thu, 17 Jul 2014 15:23:08 -0700
changeset 2538
1e39ae45d8ac
parent 2536
856dc4030eaa
child 2539
a51b7fd0543b

8029548: (jdeps) use @jdk.Exported to determine supported vs JDK internal API
8031092: jdeps does not recognize --help option.
8048063: (jdeps) Add filtering capability
Reviewed-by: alanb, dfuchs

src/share/classes/com/sun/tools/jdeps/Analyzer.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/Archive.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/ClassFileReader.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/JdepsTask.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/Main.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/PlatformClassPath.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/Profile.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties file | annotate | diff | comparison | revisions
test/tools/jdeps/APIDeps.java file | annotate | diff | comparison | revisions
test/tools/jdeps/Basic.java file | annotate | diff | comparison | revisions
test/tools/jdeps/DotFileTest.java file | annotate | diff | comparison | revisions
test/tools/jdeps/m/Gee.java file | annotate | diff | comparison | revisions
test/tools/jdeps/p/Bar.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/jdeps/Analyzer.java	Mon Jul 07 18:03:08 2014 -0700
     1.2 +++ b/src/share/classes/com/sun/tools/jdeps/Analyzer.java	Thu Jul 17 15:23:08 2014 -0700
     1.3 @@ -1,5 +1,5 @@
     1.4  /*
     1.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
     1.6 + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
     1.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.8   *
     1.9   * This code is free software; you can redistribute it and/or modify it
    1.10 @@ -24,10 +24,8 @@
    1.11   */
    1.12  package com.sun.tools.jdeps;
    1.13  
    1.14 -import com.sun.tools.classfile.Dependency.Location;
    1.15 -import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
    1.16 -import java.util.ArrayList;
    1.17  import java.util.HashMap;
    1.18 +import java.util.HashSet;
    1.19  import java.util.List;
    1.20  import java.util.Map;
    1.21  import java.util.Objects;
    1.22 @@ -37,6 +35,9 @@
    1.23  import java.util.TreeMap;
    1.24  import java.util.TreeSet;
    1.25  
    1.26 +import com.sun.tools.classfile.Dependency.Location;
    1.27 +import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
    1.28 +
    1.29  /**
    1.30   * Dependency Analyzer.
    1.31   */
    1.32 @@ -52,7 +53,16 @@
    1.33          VERBOSE
    1.34      };
    1.35  
    1.36 +    /**
    1.37 +     * Filter to be applied when analyzing the dependencies from the given archives.
    1.38 +     * Only the accepted dependencies are recorded.
    1.39 +     */
    1.40 +    interface Filter {
    1.41 +        boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
    1.42 +    }
    1.43 +
    1.44      private final Type type;
    1.45 +    private final Filter filter;
    1.46      private final Map<Archive, ArchiveDeps> results = new HashMap<>();
    1.47      private final Map<Location, Archive> map = new HashMap<>();
    1.48      private final Archive NOT_FOUND
    1.49 @@ -62,9 +72,11 @@
    1.50       * Constructs an Analyzer instance.
    1.51       *
    1.52       * @param type Type of the dependency analysis
    1.53 +     * @param filter
    1.54       */
    1.55 -    public Analyzer(Type type) {
    1.56 +    public Analyzer(Type type, Filter filter) {
    1.57          this.type = type;
    1.58 +        this.filter = filter;
    1.59      }
    1.60  
    1.61      /**
    1.62 @@ -72,6 +84,18 @@
    1.63       */
    1.64      public void run(List<Archive> archives) {
    1.65          // build a map from Location to Archive
    1.66 +        buildLocationArchiveMap(archives);
    1.67 +
    1.68 +        // traverse and analyze all dependencies
    1.69 +        for (Archive archive : archives) {
    1.70 +            ArchiveDeps deps = new ArchiveDeps(archive, type);
    1.71 +            archive.visitDependences(deps);
    1.72 +            results.put(archive, deps);
    1.73 +        }
    1.74 +    }
    1.75 +
    1.76 +    private void buildLocationArchiveMap(List<Archive> archives) {
    1.77 +        // build a map from Location to Archive
    1.78          for (Archive archive: archives) {
    1.79              for (Location l: archive.getClasses()) {
    1.80                  if (!map.containsKey(l)) {
    1.81 @@ -81,190 +105,223 @@
    1.82                  }
    1.83              }
    1.84          }
    1.85 -        // traverse and analyze all dependencies
    1.86 -        for (Archive archive : archives) {
    1.87 -            ArchiveDeps deps;
    1.88 -            if (type == Type.CLASS || type == Type.VERBOSE) {
    1.89 -                deps = new ClassVisitor(archive);
    1.90 -            } else {
    1.91 -                deps = new PackageVisitor(archive);
    1.92 -            }
    1.93 -            archive.visitDependences(deps);
    1.94 -            results.put(archive, deps);
    1.95 -        }
    1.96      }
    1.97  
    1.98      public boolean hasDependences(Archive archive) {
    1.99          if (results.containsKey(archive)) {
   1.100 -            return results.get(archive).deps.size() > 0;
   1.101 +            return results.get(archive).dependencies().size() > 0;
   1.102          }
   1.103          return false;
   1.104      }
   1.105  
   1.106      public interface Visitor {
   1.107          /**
   1.108 -         * Visits the source archive to its destination archive of
   1.109 -         * a recorded dependency.
   1.110 -         */
   1.111 -        void visitArchiveDependence(Archive origin, Archive target, Profile profile);
   1.112 -        /**
   1.113           * Visits a recorded dependency from origin to target which can be
   1.114 -         * a fully-qualified classname, a package name, a profile or
   1.115 +         * a fully-qualified classname, a package name, a module or
   1.116           * archive name depending on the Analyzer's type.
   1.117           */
   1.118 -        void visitDependence(String origin, Archive source, String target, Archive archive, Profile profile);
   1.119 +        public void visitDependence(String origin, Archive originArchive,
   1.120 +                                    String target, Archive targetArchive);
   1.121      }
   1.122  
   1.123 -    public void visitArchiveDependences(Archive source, Visitor v) {
   1.124 -        ArchiveDeps r = results.get(source);
   1.125 -        for (ArchiveDeps.Dep d: r.requireArchives()) {
   1.126 -            v.visitArchiveDependence(r.archive, d.archive, d.profile);
   1.127 +    /**
   1.128 +     * Visit the dependencies of the given source.
   1.129 +     * If the requested level is SUMMARY, it will visit the required archives list.
   1.130 +     */
   1.131 +    public void visitDependences(Archive source, Visitor v, Type level) {
   1.132 +        if (level == Type.SUMMARY) {
   1.133 +            final ArchiveDeps result = results.get(source);
   1.134 +            SortedMap<String, Archive> sorted = new TreeMap<>();
   1.135 +            for (Archive a : result.requires()) {
   1.136 +                sorted.put(a.getName(), a);
   1.137 +            }
   1.138 +            for (Archive archive : sorted.values()) {
   1.139 +                Profile profile = result.getTargetProfile(archive);
   1.140 +                v.visitDependence(source.getName(), source,
   1.141 +                                  profile != null ? profile.profileName() : archive.getName(), archive);
   1.142 +            }
   1.143 +        } else {
   1.144 +            ArchiveDeps result = results.get(source);
   1.145 +            if (level != type) {
   1.146 +                // requesting different level of analysis
   1.147 +                result = new ArchiveDeps(source, level);
   1.148 +                source.visitDependences(result);
   1.149 +            }
   1.150 +            SortedSet<Dep> sorted = new TreeSet<>(result.dependencies());
   1.151 +            for (Dep d : sorted) {
   1.152 +                v.visitDependence(d.origin(), d.originArchive(), d.target(), d.targetArchive());
   1.153 +            }
   1.154          }
   1.155      }
   1.156  
   1.157      public void visitDependences(Archive source, Visitor v) {
   1.158 -        ArchiveDeps r = results.get(source);
   1.159 -        for (Map.Entry<String, SortedSet<ArchiveDeps.Dep>> e: r.deps.entrySet()) {
   1.160 -            String origin = e.getKey();
   1.161 -            for (ArchiveDeps.Dep d: e.getValue()) {
   1.162 -                // filter intra-dependency unless in verbose mode
   1.163 -                if (type == Type.VERBOSE || d.archive != source) {
   1.164 -                    v.visitDependence(origin, source, d.target, d.archive, d.profile);
   1.165 +        visitDependences(source, v, type);
   1.166 +    }
   1.167 +
   1.168 +    /**
   1.169 +     * ArchiveDeps contains the dependencies for an Archive that can have one or
   1.170 +     * more classes.
   1.171 +     */
   1.172 +    class ArchiveDeps implements Archive.Visitor {
   1.173 +        protected final Archive archive;
   1.174 +        protected final Set<Archive> requires;
   1.175 +        protected final Set<Dep> deps;
   1.176 +        protected final Type level;
   1.177 +        private Profile profile;
   1.178 +        ArchiveDeps(Archive archive, Type level) {
   1.179 +            this.archive = archive;
   1.180 +            this.deps = new HashSet<>();
   1.181 +            this.requires = new HashSet<>();
   1.182 +            this.level = level;
   1.183 +        }
   1.184 +
   1.185 +        Set<Dep> dependencies() {
   1.186 +            return deps;
   1.187 +        }
   1.188 +
   1.189 +        Set<Archive> requires() {
   1.190 +            return requires;
   1.191 +        }
   1.192 +
   1.193 +        Profile getTargetProfile(Archive target) {
   1.194 +            return JDKArchive.isProfileArchive(target) ? profile : null;
   1.195 +        }
   1.196 +
   1.197 +        Archive findArchive(Location t) {
   1.198 +            Archive target = archive.getClasses().contains(t) ? archive : map.get(t);
   1.199 +            if (target == null) {
   1.200 +                map.put(t, target = NOT_FOUND);
   1.201 +            }
   1.202 +            return target;
   1.203 +        }
   1.204 +
   1.205 +        // return classname or package name depedning on the level
   1.206 +        private String getLocationName(Location o) {
   1.207 +            if (level == Type.CLASS || level == Type.VERBOSE) {
   1.208 +                return o.getClassName();
   1.209 +            } else {
   1.210 +                String pkg = o.getPackageName();
   1.211 +                return pkg.isEmpty() ? "<unnamed>" : pkg;
   1.212 +            }
   1.213 +        }
   1.214 +
   1.215 +        @Override
   1.216 +        public void visit(Location o, Location t) {
   1.217 +            Archive targetArchive = findArchive(t);
   1.218 +            if (filter.accepts(o, archive, t, targetArchive)) {
   1.219 +                addDep(o, t);
   1.220 +                if (!requires.contains(targetArchive)) {
   1.221 +                    requires.add(targetArchive);
   1.222 +                }
   1.223 +            }
   1.224 +            if (targetArchive instanceof JDKArchive) {
   1.225 +                Profile p = Profile.getProfile(t.getPackageName());
   1.226 +                if (profile == null || (p != null && p.compareTo(profile) > 0)) {
   1.227 +                    profile = p;
   1.228                  }
   1.229              }
   1.230          }
   1.231 -    }
   1.232  
   1.233 -    /**
   1.234 -     * ArchiveDeps contains the dependencies for an Archive that
   1.235 -     * can have one or more classes.
   1.236 -     */
   1.237 -    private abstract class ArchiveDeps implements Archive.Visitor {
   1.238 -        final Archive archive;
   1.239 -        final SortedMap<String, SortedSet<Dep>> deps;
   1.240 -        ArchiveDeps(Archive archive) {
   1.241 -            this.archive = archive;
   1.242 -            this.deps = new TreeMap<>();
   1.243 -        }
   1.244 +        private Dep curDep;
   1.245 +        protected Dep addDep(Location o, Location t) {
   1.246 +            String origin = getLocationName(o);
   1.247 +            String target = getLocationName(t);
   1.248 +            Archive targetArchive = findArchive(t);
   1.249 +            if (curDep != null &&
   1.250 +                    curDep.origin().equals(origin) &&
   1.251 +                    curDep.originArchive() == archive &&
   1.252 +                    curDep.target().equals(target) &&
   1.253 +                    curDep.targetArchive() == targetArchive) {
   1.254 +                return curDep;
   1.255 +            }
   1.256  
   1.257 -        void add(String origin, String target, Archive targetArchive, String pkgName) {
   1.258 -            SortedSet<Dep> set = deps.get(origin);
   1.259 -            if (set == null) {
   1.260 -                deps.put(origin, set = new TreeSet<>());
   1.261 -            }
   1.262 -            Profile p = targetArchive instanceof JDKArchive
   1.263 -                            ? Profile.getProfile(pkgName) : null;
   1.264 -            set.add(new Dep(target, targetArchive, p));
   1.265 -        }
   1.266 -
   1.267 -        /**
   1.268 -         * Returns the list of Archive dependences.  The returned
   1.269 -         * list contains one {@code Dep} instance per one archive
   1.270 -         * and with the minimum profile this archive depends on.
   1.271 -         */
   1.272 -        List<Dep> requireArchives() {
   1.273 -            Map<Archive,Profile> map = new HashMap<>();
   1.274 -            for (Set<Dep> set: deps.values()) {
   1.275 -                for (Dep d: set) {
   1.276 -                    if (this.archive != d.archive) {
   1.277 -                        Profile p = map.get(d.archive);
   1.278 -                        if (p == null || (d.profile != null && p.profile < d.profile.profile)) {
   1.279 -                            map.put(d.archive, d.profile);
   1.280 -                        }
   1.281 +            Dep e = new Dep(origin, archive, target, targetArchive);
   1.282 +            if (deps.contains(e)) {
   1.283 +                for (Dep e1 : deps) {
   1.284 +                    if (e.equals(e1)) {
   1.285 +                        curDep = e1;
   1.286                      }
   1.287                  }
   1.288 +            } else {
   1.289 +                deps.add(e);
   1.290 +                curDep = e;
   1.291              }
   1.292 -            List<Dep> list = new ArrayList<>();
   1.293 -            for (Map.Entry<Archive,Profile> e: map.entrySet()) {
   1.294 -                list.add(new Dep("", e.getKey(), e.getValue()));
   1.295 -            }
   1.296 -            return list;
   1.297 -        }
   1.298 -
   1.299 -        /**
   1.300 -         * Dep represents a dependence where the target can be
   1.301 -         * a classname or packagename and the archive and profile
   1.302 -         * the target belongs to.
   1.303 -         */
   1.304 -        class Dep implements Comparable<Dep> {
   1.305 -            final String target;
   1.306 -            final Archive archive;
   1.307 -            final Profile profile;
   1.308 -            Dep(String target, Archive archive, Profile p) {
   1.309 -                this.target = target;
   1.310 -                this.archive = archive;
   1.311 -                this.profile = p;
   1.312 -            }
   1.313 -
   1.314 -            @Override
   1.315 -            public boolean equals(Object o) {
   1.316 -                if (o instanceof Dep) {
   1.317 -                    Dep d = (Dep)o;
   1.318 -                    return this.archive == d.archive && this.target.equals(d.target);
   1.319 -                }
   1.320 -                return false;
   1.321 -            }
   1.322 -
   1.323 -            @Override
   1.324 -            public int hashCode() {
   1.325 -                int hash = 3;
   1.326 -                hash = 17 * hash + Objects.hashCode(this.archive);
   1.327 -                hash = 17 * hash + Objects.hashCode(this.target);
   1.328 -                return hash;
   1.329 -            }
   1.330 -
   1.331 -            @Override
   1.332 -            public int compareTo(Dep o) {
   1.333 -                if (this.target.equals(o.target)) {
   1.334 -                    if (this.archive == o.archive) {
   1.335 -                        return 0;
   1.336 -                    } else {
   1.337 -                        return this.archive.getFileName().compareTo(o.archive.getFileName());
   1.338 -                    }
   1.339 -                }
   1.340 -                return this.target.compareTo(o.target);
   1.341 -            }
   1.342 -        }
   1.343 -        public abstract void visit(Location o, Location t);
   1.344 -    }
   1.345 -
   1.346 -    private class ClassVisitor extends ArchiveDeps {
   1.347 -        ClassVisitor(Archive archive) {
   1.348 -            super(archive);
   1.349 -        }
   1.350 -        @Override
   1.351 -        public void visit(Location o, Location t) {
   1.352 -            Archive targetArchive =
   1.353 -                this.archive.getClasses().contains(t) ? this.archive : map.get(t);
   1.354 -            if (targetArchive == null) {
   1.355 -                map.put(t, targetArchive = NOT_FOUND);
   1.356 -            }
   1.357 -
   1.358 -            String origin = o.getClassName();
   1.359 -            String target = t.getClassName();
   1.360 -            add(origin, target, targetArchive, t.getPackageName());
   1.361 +            return curDep;
   1.362          }
   1.363      }
   1.364  
   1.365 -    private class PackageVisitor extends ArchiveDeps {
   1.366 -        PackageVisitor(Archive archive) {
   1.367 -            super(archive);
   1.368 +    /*
   1.369 +     * Class-level or package-level dependency
   1.370 +     */
   1.371 +    class Dep implements Comparable<Dep> {
   1.372 +        final String origin;
   1.373 +        final Archive originArchive;
   1.374 +        final String target;
   1.375 +        final Archive targetArchive;
   1.376 +
   1.377 +        Dep(String origin, Archive originArchive, String target, Archive targetArchive) {
   1.378 +            this.origin = origin;
   1.379 +            this.originArchive = originArchive;
   1.380 +            this.target = target;
   1.381 +            this.targetArchive = targetArchive;
   1.382          }
   1.383 +
   1.384 +        String origin() {
   1.385 +            return origin;
   1.386 +        }
   1.387 +
   1.388 +        Archive originArchive() {
   1.389 +            return originArchive;
   1.390 +        }
   1.391 +
   1.392 +        String target() {
   1.393 +            return target;
   1.394 +        }
   1.395 +
   1.396 +        Archive targetArchive() {
   1.397 +            return targetArchive;
   1.398 +        }
   1.399 +
   1.400          @Override
   1.401 -        public void visit(Location o, Location t) {
   1.402 -            Archive targetArchive =
   1.403 -                this.archive.getClasses().contains(t) ? this.archive : map.get(t);
   1.404 -            if (targetArchive == null) {
   1.405 -                map.put(t, targetArchive = NOT_FOUND);
   1.406 +        @SuppressWarnings("unchecked")
   1.407 +        public boolean equals(Object o) {
   1.408 +            if (o instanceof Dep) {
   1.409 +                Dep d = (Dep) o;
   1.410 +                return this.origin.equals(d.origin) &&
   1.411 +                        this.originArchive == d.originArchive &&
   1.412 +                        this.target.equals(d.target) &&
   1.413 +                        this.targetArchive == d.targetArchive;
   1.414              }
   1.415 +            return false;
   1.416 +        }
   1.417  
   1.418 -            String origin = packageOf(o);
   1.419 -            String target = packageOf(t);
   1.420 -            add(origin, target, targetArchive, t.getPackageName());
   1.421 +        @Override
   1.422 +        public int hashCode() {
   1.423 +            int hash = 7;
   1.424 +            hash = 67*hash + Objects.hashCode(this.origin)
   1.425 +                           + Objects.hashCode(this.originArchive)
   1.426 +                           + Objects.hashCode(this.target)
   1.427 +                           + Objects.hashCode(this.targetArchive);
   1.428 +            return hash;
   1.429          }
   1.430 -        public String packageOf(Location o) {
   1.431 -            String pkg = o.getPackageName();
   1.432 -            return pkg.isEmpty() ? "<unnamed>" : pkg;
   1.433 +
   1.434 +        @Override
   1.435 +        public int compareTo(Dep o) {
   1.436 +            if (this.origin.equals(o.origin)) {
   1.437 +                if (this.target.equals(o.target)) {
   1.438 +                    if (this.originArchive == o.originArchive &&
   1.439 +                            this.targetArchive == o.targetArchive) {
   1.440 +                        return 0;
   1.441 +                    } else if (this.originArchive == o.originArchive) {
   1.442 +                        return this.targetArchive.getPathName().compareTo(o.targetArchive.getPathName());
   1.443 +                    } else {
   1.444 +                        return this.originArchive.getPathName().compareTo(o.originArchive.getPathName());
   1.445 +                    }
   1.446 +                } else {
   1.447 +                    return this.target.compareTo(o.target);
   1.448 +                }
   1.449 +            }
   1.450 +            return this.origin.compareTo(o.origin);
   1.451          }
   1.452      }
   1.453  }
     2.1 --- a/src/share/classes/com/sun/tools/jdeps/Archive.java	Mon Jul 07 18:03:08 2014 -0700
     2.2 +++ b/src/share/classes/com/sun/tools/jdeps/Archive.java	Thu Jul 17 15:23:08 2014 -0700
     2.3 @@ -1,5 +1,5 @@
     2.4  /*
     2.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     2.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
     2.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     2.8   *
     2.9   * This code is free software; you can redistribute it and/or modify it
    2.10 @@ -25,28 +25,34 @@
    2.11  package com.sun.tools.jdeps;
    2.12  
    2.13  import com.sun.tools.classfile.Dependency.Location;
    2.14 +
    2.15 +import java.io.IOException;
    2.16  import java.nio.file.Path;
    2.17 -import java.util.HashMap;
    2.18  import java.util.HashSet;
    2.19  import java.util.Map;
    2.20  import java.util.Set;
    2.21 +import java.util.concurrent.ConcurrentHashMap;
    2.22  
    2.23  /**
    2.24   * Represents the source of the class files.
    2.25   */
    2.26  public class Archive {
    2.27 +    public static Archive getInstance(Path p) throws IOException {
    2.28 +        return new Archive(p, ClassFileReader.newInstance(p));
    2.29 +    }
    2.30 +
    2.31      private final Path path;
    2.32      private final String filename;
    2.33      private final ClassFileReader reader;
    2.34 -    private final Map<Location, Set<Location>> deps = new HashMap<>();
    2.35 +    protected Map<Location, Set<Location>> deps = new ConcurrentHashMap<>();
    2.36  
    2.37 -    public Archive(String name) {
    2.38 +    protected Archive(String name) {
    2.39          this.path = null;
    2.40          this.filename = name;
    2.41          this.reader = null;
    2.42      }
    2.43  
    2.44 -    public Archive(Path p, ClassFileReader reader) {
    2.45 +    protected Archive(Path p, ClassFileReader reader) {
    2.46          this.path = p;
    2.47          this.filename = path.getFileName().toString();
    2.48          this.reader = reader;
    2.49 @@ -56,7 +62,7 @@
    2.50          return reader;
    2.51      }
    2.52  
    2.53 -    public String getFileName() {
    2.54 +    public String getName() {
    2.55          return filename;
    2.56      }
    2.57  
    2.58 @@ -89,6 +95,10 @@
    2.59          }
    2.60      }
    2.61  
    2.62 +    public boolean isEmpty() {
    2.63 +        return getClasses().isEmpty();
    2.64 +    }
    2.65 +
    2.66      public String getPathName() {
    2.67          return path != null ? path.toString() : filename;
    2.68      }
     3.1 --- a/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java	Mon Jul 07 18:03:08 2014 -0700
     3.2 +++ b/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java	Thu Jul 17 15:23:08 2014 -0700
     3.3 @@ -1,5 +1,5 @@
     3.4  /*
     3.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     3.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
     3.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.8   *
     3.9   * This code is free software; you can redistribute it and/or modify it
    3.10 @@ -68,7 +68,8 @@
    3.11  
    3.12      protected final Path path;
    3.13      protected final String baseFileName;
    3.14 -    private ClassFileReader(Path path) {
    3.15 +    protected final List<String> skippedEntries = new ArrayList<>();
    3.16 +    protected ClassFileReader(Path path) {
    3.17          this.path = path;
    3.18          this.baseFileName = path.getFileName() != null
    3.19                                  ? path.getFileName().toString()
    3.20 @@ -79,6 +80,10 @@
    3.21          return baseFileName;
    3.22      }
    3.23  
    3.24 +    public List<String> skippedEntries() {
    3.25 +        return skippedEntries;
    3.26 +    }
    3.27 +
    3.28      /**
    3.29       * Returns the ClassFile matching the given binary name
    3.30       * or a fully-qualified class name.
    3.31 @@ -232,11 +237,12 @@
    3.32          }
    3.33      }
    3.34  
    3.35 -    private static class JarFileReader extends ClassFileReader {
    3.36 -        final JarFile jarfile;
    3.37 +    static class JarFileReader extends ClassFileReader {
    3.38 +        private final JarFile jarfile;
    3.39          JarFileReader(Path path) throws IOException {
    3.40 -            this(path, new JarFile(path.toFile()));
    3.41 +            this(path, new JarFile(path.toFile(), false));
    3.42          }
    3.43 +
    3.44          JarFileReader(Path path, JarFile jf) throws IOException {
    3.45              super(path);
    3.46              this.jarfile = jf;
    3.47 @@ -252,18 +258,18 @@
    3.48                              + entryName.substring(i + 1, entryName.length()));
    3.49                  }
    3.50                  if (e != null) {
    3.51 -                    return readClassFile(e);
    3.52 +                    return readClassFile(jarfile, e);
    3.53                  }
    3.54              } else {
    3.55                  JarEntry e = jarfile.getJarEntry(name + ".class");
    3.56                  if (e != null) {
    3.57 -                    return readClassFile(e);
    3.58 +                    return readClassFile(jarfile, e);
    3.59                  }
    3.60              }
    3.61              return null;
    3.62          }
    3.63  
    3.64 -        private ClassFile readClassFile(JarEntry e) throws IOException {
    3.65 +        protected ClassFile readClassFile(JarFile jarfile, JarEntry e) throws IOException {
    3.66              InputStream is = null;
    3.67              try {
    3.68                  is = jarfile.getInputStream(e);
    3.69 @@ -277,60 +283,76 @@
    3.70          }
    3.71  
    3.72          public Iterable<ClassFile> getClassFiles() throws IOException {
    3.73 -            final Iterator<ClassFile> iter = new JarFileIterator();
    3.74 +            final Iterator<ClassFile> iter = new JarFileIterator(this, jarfile);
    3.75              return new Iterable<ClassFile>() {
    3.76                  public Iterator<ClassFile> iterator() {
    3.77                      return iter;
    3.78                  }
    3.79              };
    3.80          }
    3.81 +    }
    3.82  
    3.83 -        class JarFileIterator implements Iterator<ClassFile> {
    3.84 -            private Enumeration<JarEntry> entries;
    3.85 -            private JarEntry nextEntry;
    3.86 -            JarFileIterator() {
    3.87 -                this.entries = jarfile.entries();
    3.88 -                while (entries.hasMoreElements()) {
    3.89 -                    JarEntry e = entries.nextElement();
    3.90 -                    String name = e.getName();
    3.91 -                    if (name.endsWith(".class")) {
    3.92 -                        this.nextEntry = e;
    3.93 -                        break;
    3.94 -                    }
    3.95 +    class JarFileIterator implements Iterator<ClassFile> {
    3.96 +        protected final JarFileReader reader;
    3.97 +        protected Enumeration<JarEntry> entries;
    3.98 +        protected JarFile jf;
    3.99 +        protected JarEntry nextEntry;
   3.100 +        protected ClassFile cf;
   3.101 +        JarFileIterator(JarFileReader reader) {
   3.102 +            this(reader, null);
   3.103 +        }
   3.104 +        JarFileIterator(JarFileReader reader, JarFile jarfile) {
   3.105 +            this.reader = reader;
   3.106 +            setJarFile(jarfile);
   3.107 +        }
   3.108 +
   3.109 +        void setJarFile(JarFile jarfile) {
   3.110 +            if (jarfile == null) return;
   3.111 +
   3.112 +            this.jf = jarfile;
   3.113 +            this.entries = jf.entries();
   3.114 +            this.nextEntry = nextEntry();
   3.115 +        }
   3.116 +
   3.117 +        public boolean hasNext() {
   3.118 +            if (nextEntry != null && cf != null) {
   3.119 +                return true;
   3.120 +            }
   3.121 +            while (nextEntry != null) {
   3.122 +                try {
   3.123 +                    cf = reader.readClassFile(jf, nextEntry);
   3.124 +                    return true;
   3.125 +                } catch (ClassFileError | IOException ex) {
   3.126 +                    skippedEntries.add(nextEntry.getName());
   3.127 +                }
   3.128 +                nextEntry = nextEntry();
   3.129 +            }
   3.130 +            return false;
   3.131 +        }
   3.132 +
   3.133 +        public ClassFile next() {
   3.134 +            if (!hasNext()) {
   3.135 +                throw new NoSuchElementException();
   3.136 +            }
   3.137 +            ClassFile classFile = cf;
   3.138 +            cf = null;
   3.139 +            nextEntry = nextEntry();
   3.140 +            return classFile;
   3.141 +        }
   3.142 +
   3.143 +        protected JarEntry nextEntry() {
   3.144 +            while (entries.hasMoreElements()) {
   3.145 +                JarEntry e = entries.nextElement();
   3.146 +                String name = e.getName();
   3.147 +                if (name.endsWith(".class")) {
   3.148 +                    return e;
   3.149                  }
   3.150              }
   3.151 +            return null;
   3.152 +        }
   3.153  
   3.154 -            public boolean hasNext() {
   3.155 -                return nextEntry != null;
   3.156 -            }
   3.157 -
   3.158 -            public ClassFile next() {
   3.159 -                if (!hasNext()) {
   3.160 -                    throw new NoSuchElementException();
   3.161 -                }
   3.162 -
   3.163 -                ClassFile cf;
   3.164 -                try {
   3.165 -                    cf = readClassFile(nextEntry);
   3.166 -                } catch (IOException ex) {
   3.167 -                    throw new ClassFileError(ex);
   3.168 -                }
   3.169 -                JarEntry entry = nextEntry;
   3.170 -                nextEntry = null;
   3.171 -                while (entries.hasMoreElements()) {
   3.172 -                    JarEntry e = entries.nextElement();
   3.173 -                    String name = e.getName();
   3.174 -                    if (name.endsWith(".class")) {
   3.175 -                        nextEntry = e;
   3.176 -                        break;
   3.177 -                    }
   3.178 -                }
   3.179 -                return cf;
   3.180 -            }
   3.181 -
   3.182 -            public void remove() {
   3.183 -                throw new UnsupportedOperationException("Not supported yet.");
   3.184 -            }
   3.185 +        public void remove() {
   3.186 +            throw new UnsupportedOperationException("Not supported yet.");
   3.187          }
   3.188      }
   3.189  }
     4.1 --- a/src/share/classes/com/sun/tools/jdeps/JdepsTask.java	Mon Jul 07 18:03:08 2014 -0700
     4.2 +++ b/src/share/classes/com/sun/tools/jdeps/JdepsTask.java	Thu Jul 17 15:23:08 2014 -0700
     4.3 @@ -1,5 +1,5 @@
     4.4  /*
     4.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     4.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
     4.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4.8   *
     4.9   * This code is free software; you can redistribute it and/or modify it
    4.10 @@ -30,7 +30,9 @@
    4.11  import com.sun.tools.classfile.Dependencies;
    4.12  import com.sun.tools.classfile.Dependencies.ClassFileError;
    4.13  import com.sun.tools.classfile.Dependency;
    4.14 +import com.sun.tools.classfile.Dependency.Location;
    4.15  import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
    4.16 +import static com.sun.tools.jdeps.Analyzer.Type.*;
    4.17  import java.io.*;
    4.18  import java.nio.file.DirectoryStream;
    4.19  import java.nio.file.Files;
    4.20 @@ -110,7 +112,7 @@
    4.21              void process(JdepsTask task, String opt, String arg) throws BadArgs {
    4.22                  Path p = Paths.get(arg);
    4.23                  if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
    4.24 -                    throw new BadArgs("err.dot.output.path", arg);
    4.25 +                    throw new BadArgs("err.invalid.path", arg);
    4.26                  }
    4.27                  task.options.dotOutputDir = arg;
    4.28              }
    4.29 @@ -118,25 +120,26 @@
    4.30          new Option(false, "-s", "-summary") {
    4.31              void process(JdepsTask task, String opt, String arg) {
    4.32                  task.options.showSummary = true;
    4.33 -                task.options.verbose = Analyzer.Type.SUMMARY;
    4.34 +                task.options.verbose = SUMMARY;
    4.35              }
    4.36          },
    4.37          new Option(false, "-v", "-verbose",
    4.38                            "-verbose:package",
    4.39 -                          "-verbose:class")
    4.40 -        {
    4.41 +                          "-verbose:class") {
    4.42              void process(JdepsTask task, String opt, String arg) throws BadArgs {
    4.43                  switch (opt) {
    4.44                      case "-v":
    4.45                      case "-verbose":
    4.46 -                        task.options.verbose = Analyzer.Type.VERBOSE;
    4.47 +                        task.options.verbose = VERBOSE;
    4.48 +                        task.options.filterSameArchive = false;
    4.49 +                        task.options.filterSamePackage = false;
    4.50                          break;
    4.51                      case "-verbose:package":
    4.52 -                            task.options.verbose = Analyzer.Type.PACKAGE;
    4.53 -                            break;
    4.54 +                        task.options.verbose = PACKAGE;
    4.55 +                        break;
    4.56                      case "-verbose:class":
    4.57 -                            task.options.verbose = Analyzer.Type.CLASS;
    4.58 -                            break;
    4.59 +                        task.options.verbose = CLASS;
    4.60 +                        break;
    4.61                      default:
    4.62                          throw new BadArgs("err.invalid.arg.for.option", opt);
    4.63                  }
    4.64 @@ -157,6 +160,32 @@
    4.65                  task.options.regex = arg;
    4.66              }
    4.67          },
    4.68 +
    4.69 +        new Option(true, "-f", "-filter") {
    4.70 +            void process(JdepsTask task, String opt, String arg) {
    4.71 +                task.options.filterRegex = arg;
    4.72 +            }
    4.73 +        },
    4.74 +        new Option(false, "-filter:package",
    4.75 +                          "-filter:archive",
    4.76 +                          "-filter:none") {
    4.77 +            void process(JdepsTask task, String opt, String arg) {
    4.78 +                switch (opt) {
    4.79 +                    case "-filter:package":
    4.80 +                        task.options.filterSamePackage = true;
    4.81 +                        task.options.filterSameArchive = false;
    4.82 +                        break;
    4.83 +                    case "-filter:archive":
    4.84 +                        task.options.filterSameArchive = true;
    4.85 +                        task.options.filterSamePackage = false;
    4.86 +                        break;
    4.87 +                    case "-filter:none":
    4.88 +                        task.options.filterSameArchive = false;
    4.89 +                        task.options.filterSamePackage = false;
    4.90 +                        break;
    4.91 +                }
    4.92 +            }
    4.93 +        },
    4.94          new Option(true, "-include") {
    4.95              void process(JdepsTask task, String opt, String arg) throws BadArgs {
    4.96                  task.options.includePattern = Pattern.compile(arg);
    4.97 @@ -178,12 +207,15 @@
    4.98          new Option(false, "-R", "-recursive") {
    4.99              void process(JdepsTask task, String opt, String arg) {
   4.100                  task.options.depth = 0;
   4.101 +                // turn off filtering
   4.102 +                task.options.filterSameArchive = false;
   4.103 +                task.options.filterSamePackage = false;
   4.104              }
   4.105          },
   4.106          new Option(false, "-jdkinternals") {
   4.107              void process(JdepsTask task, String opt, String arg) {
   4.108                  task.options.findJDKInternals = true;
   4.109 -                task.options.verbose = Analyzer.Type.CLASS;
   4.110 +                task.options.verbose = CLASS;
   4.111                  if (task.options.includePattern == null) {
   4.112                      task.options.includePattern = Pattern.compile(".*");
   4.113                  }
   4.114 @@ -262,7 +294,7 @@
   4.115                  showHelp();
   4.116                  return EXIT_CMDERR;
   4.117              }
   4.118 -            if (options.showSummary && options.verbose != Analyzer.Type.SUMMARY) {
   4.119 +            if (options.showSummary && options.verbose != SUMMARY) {
   4.120                  showHelp();
   4.121                  return EXIT_CMDERR;
   4.122              }
   4.123 @@ -283,9 +315,28 @@
   4.124  
   4.125      private final List<Archive> sourceLocations = new ArrayList<>();
   4.126      private boolean run() throws IOException {
   4.127 +        // parse classfiles and find all dependencies
   4.128          findDependencies();
   4.129 -        Analyzer analyzer = new Analyzer(options.verbose);
   4.130 +
   4.131 +        Analyzer analyzer = new Analyzer(options.verbose, new Analyzer.Filter() {
   4.132 +            @Override
   4.133 +            public boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive) {
   4.134 +                if (options.findJDKInternals) {
   4.135 +                    // accepts target that is JDK class but not exported
   4.136 +                    return isJDKArchive(targetArchive) &&
   4.137 +                              !((JDKArchive) targetArchive).isExported(target.getClassName());
   4.138 +                } else if (options.filterSameArchive) {
   4.139 +                    // accepts origin and target that from different archive
   4.140 +                    return originArchive != targetArchive;
   4.141 +                }
   4.142 +                return true;
   4.143 +            }
   4.144 +        });
   4.145 +
   4.146 +        // analyze the dependencies
   4.147          analyzer.run(sourceLocations);
   4.148 +
   4.149 +        // output result
   4.150          if (options.dotOutputDir != null) {
   4.151              Path dir = Paths.get(options.dotOutputDir);
   4.152              Files.createDirectories(dir);
   4.153 @@ -296,27 +347,34 @@
   4.154          return true;
   4.155      }
   4.156  
   4.157 -    private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException {
   4.158 +    private void generateSummaryDotFile(Path dir, Analyzer analyzer) throws IOException {
   4.159 +        // If verbose mode (-v or -verbose option),
   4.160 +        // the summary.dot file shows package-level dependencies.
   4.161 +        Analyzer.Type summaryType =
   4.162 +            (options.verbose == PACKAGE || options.verbose == SUMMARY) ? SUMMARY : PACKAGE;
   4.163          Path summary = dir.resolve("summary.dot");
   4.164 -        boolean verbose = options.verbose == Analyzer.Type.VERBOSE;
   4.165 -        DotGraph<?> graph = verbose ? new DotSummaryForPackage()
   4.166 -                                    : new DotSummaryForArchive();
   4.167 -        for (Archive archive : sourceLocations) {
   4.168 -            analyzer.visitArchiveDependences(archive, graph);
   4.169 -            if (verbose || options.showLabel) {
   4.170 -                // traverse detailed dependences to generate package-level
   4.171 -                // summary or build labels for edges
   4.172 -                analyzer.visitDependences(archive, graph);
   4.173 +        try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
   4.174 +             SummaryDotFile dotfile = new SummaryDotFile(sw, summaryType)) {
   4.175 +            for (Archive archive : sourceLocations) {
   4.176 +                if (!archive.isEmpty()) {
   4.177 +                    if (options.verbose == PACKAGE || options.verbose == SUMMARY) {
   4.178 +                        if (options.showLabel) {
   4.179 +                            // build labels listing package-level dependencies
   4.180 +                            analyzer.visitDependences(archive, dotfile.labelBuilder(), PACKAGE);
   4.181 +                        }
   4.182 +                    }
   4.183 +                    analyzer.visitDependences(archive, dotfile, summaryType);
   4.184 +                }
   4.185              }
   4.186          }
   4.187 -        try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary))) {
   4.188 -            graph.writeTo(sw);
   4.189 -        }
   4.190 +    }
   4.191 +
   4.192 +    private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException {
   4.193          // output individual .dot file for each archive
   4.194 -        if (options.verbose != Analyzer.Type.SUMMARY) {
   4.195 +        if (options.verbose != SUMMARY) {
   4.196              for (Archive archive : sourceLocations) {
   4.197                  if (analyzer.hasDependences(archive)) {
   4.198 -                    Path dotfile = dir.resolve(archive.getFileName() + ".dot");
   4.199 +                    Path dotfile = dir.resolve(archive.getName() + ".dot");
   4.200                      try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile));
   4.201                           DotFileFormatter formatter = new DotFileFormatter(pw, archive)) {
   4.202                          analyzer.visitDependences(archive, formatter);
   4.203 @@ -324,17 +382,23 @@
   4.204                  }
   4.205              }
   4.206          }
   4.207 +        // generate summary dot file
   4.208 +        generateSummaryDotFile(dir, analyzer);
   4.209      }
   4.210  
   4.211      private void printRawOutput(PrintWriter writer, Analyzer analyzer) {
   4.212 +        RawOutputFormatter depFormatter = new RawOutputFormatter(writer);
   4.213 +        RawSummaryFormatter summaryFormatter = new RawSummaryFormatter(writer);
   4.214          for (Archive archive : sourceLocations) {
   4.215 -            RawOutputFormatter formatter = new RawOutputFormatter(writer);
   4.216 -            analyzer.visitArchiveDependences(archive, formatter);
   4.217 -            if (options.verbose != Analyzer.Type.SUMMARY) {
   4.218 -                analyzer.visitDependences(archive, formatter);
   4.219 +            if (!archive.isEmpty()) {
   4.220 +                analyzer.visitDependences(archive, summaryFormatter, SUMMARY);
   4.221 +                if (analyzer.hasDependences(archive) && options.verbose != SUMMARY) {
   4.222 +                    analyzer.visitDependences(archive, depFormatter);
   4.223 +                }
   4.224              }
   4.225          }
   4.226      }
   4.227 +
   4.228      private boolean isValidClassName(String name) {
   4.229          if (!Character.isJavaIdentifierStart(name.charAt(0))) {
   4.230              return false;
   4.231 @@ -348,21 +412,54 @@
   4.232          return true;
   4.233      }
   4.234  
   4.235 -    private Dependency.Filter getDependencyFilter() {
   4.236 -         if (options.regex != null) {
   4.237 -            return Dependencies.getRegexFilter(Pattern.compile(options.regex));
   4.238 -        } else if (options.packageNames.size() > 0) {
   4.239 -            return Dependencies.getPackageFilter(options.packageNames, false);
   4.240 -        } else {
   4.241 -            return new Dependency.Filter() {
   4.242 -                @Override
   4.243 -                public boolean accepts(Dependency dependency) {
   4.244 -                    return !dependency.getOrigin().equals(dependency.getTarget());
   4.245 -                }
   4.246 -            };
   4.247 +    /*
   4.248 +     * Dep Filter configured based on the input jdeps option
   4.249 +     * 1. -p and -regex to match target dependencies
   4.250 +     * 2. -filter:package to filter out same-package dependencies
   4.251 +     *
   4.252 +     * This filter is applied when jdeps parses the class files
   4.253 +     * and filtered dependencies are not stored in the Analyzer.
   4.254 +     *
   4.255 +     * -filter:archive is applied later in the Analyzer as the
   4.256 +     * containing archive of a target class may not be known until
   4.257 +     * the entire archive
   4.258 +     */
   4.259 +    class DependencyFilter implements Dependency.Filter {
   4.260 +        final Dependency.Filter filter;
   4.261 +        final Pattern filterPattern;
   4.262 +        DependencyFilter() {
   4.263 +            if (options.regex != null) {
   4.264 +                this.filter = Dependencies.getRegexFilter(Pattern.compile(options.regex));
   4.265 +            } else if (options.packageNames.size() > 0) {
   4.266 +                this.filter = Dependencies.getPackageFilter(options.packageNames, false);
   4.267 +            } else {
   4.268 +                this.filter = null;
   4.269 +            }
   4.270 +
   4.271 +            this.filterPattern =
   4.272 +                options.filterRegex != null ? Pattern.compile(options.filterRegex) : null;
   4.273 +        }
   4.274 +        @Override
   4.275 +        public boolean accepts(Dependency d) {
   4.276 +            if (d.getOrigin().equals(d.getTarget())) {
   4.277 +                return false;
   4.278 +            }
   4.279 +            String pn = d.getTarget().getPackageName();
   4.280 +            if (options.filterSamePackage && d.getOrigin().getPackageName().equals(pn)) {
   4.281 +                return false;
   4.282 +            }
   4.283 +
   4.284 +            if (filterPattern != null && filterPattern.matcher(pn).matches()) {
   4.285 +                return false;
   4.286 +            }
   4.287 +            return filter != null ? filter.accepts(d) : true;
   4.288          }
   4.289      }
   4.290  
   4.291 +    /**
   4.292 +     * Tests if the given class matches the pattern given in the -include option
   4.293 +     * or if it's a public class if -apionly option is specified
   4.294 +     */
   4.295      private boolean matches(String classname, AccessFlags flags) {
   4.296          if (options.apiOnly && !flags.is(AccessFlags.ACC_PUBLIC)) {
   4.297              return false;
   4.298 @@ -377,14 +474,14 @@
   4.299          Dependency.Finder finder =
   4.300              options.apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
   4.301                              : Dependencies.getClassDependencyFinder();
   4.302 -        Dependency.Filter filter = getDependencyFilter();
   4.303 +        Dependency.Filter filter = new DependencyFilter();
   4.304  
   4.305          List<Archive> archives = new ArrayList<>();
   4.306          Deque<String> roots = new LinkedList<>();
   4.307          for (String s : classes) {
   4.308              Path p = Paths.get(s);
   4.309              if (Files.exists(p)) {
   4.310 -                archives.add(new Archive(p, ClassFileReader.newInstance(p)));
   4.311 +                archives.add(Archive.getInstance(p));
   4.312              } else {
   4.313                  if (isValidClassName(s)) {
   4.314                      roots.add(s);
   4.315 @@ -421,19 +518,26 @@
   4.316                      throw new ClassFileError(e);
   4.317                  }
   4.318  
   4.319 -                if (matches(classFileName, cf.access_flags)) {
   4.320 -                    if (!doneClasses.contains(classFileName)) {
   4.321 -                        doneClasses.add(classFileName);
   4.322 +                // tests if this class matches the -include or -apiOnly option if specified
   4.323 +                if (!matches(classFileName, cf.access_flags)) {
   4.324 +                    continue;
   4.325 +                }
   4.326 +
   4.327 +                if (!doneClasses.contains(classFileName)) {
   4.328 +                    doneClasses.add(classFileName);
   4.329 +                }
   4.330 +
   4.331 +                for (Dependency d : finder.findDependencies(cf)) {
   4.332 +                    if (filter.accepts(d)) {
   4.333 +                        String cn = d.getTarget().getName();
   4.334 +                        if (!doneClasses.contains(cn) && !deque.contains(cn)) {
   4.335 +                            deque.add(cn);
   4.336 +                        }
   4.337 +                        a.addClass(d.getOrigin(), d.getTarget());
   4.338                      }
   4.339 -                    for (Dependency d : finder.findDependencies(cf)) {
   4.340 -                        if (filter.accepts(d)) {
   4.341 -                            String cn = d.getTarget().getName();
   4.342 -                            if (!doneClasses.contains(cn) && !deque.contains(cn)) {
   4.343 -                                deque.add(cn);
   4.344 -                            }
   4.345 -                            a.addClass(d.getOrigin(), d.getTarget());
   4.346 -                        }
   4.347 -                    }
   4.348 +                }
   4.349 +                for (String name : a.reader().skippedEntries()) {
   4.350 +                    warning("warn.skipped.entry", name, a.getPathName());
   4.351                  }
   4.352              }
   4.353          }
   4.354 @@ -462,6 +566,10 @@
   4.355                              // if name is a fully-qualified class name specified
   4.356                              // from command-line, this class might already be parsed
   4.357                              doneClasses.add(classFileName);
   4.358 +                            // process @jdk.Exported for JDK classes
   4.359 +                            if (isJDKArchive(a)) {
   4.360 +                                ((JDKArchive)a).processJdkExported(cf);
   4.361 +                            }
   4.362                              for (Dependency d : finder.findDependencies(cf)) {
   4.363                                  if (depth == 0) {
   4.364                                      // ignore the dependency
   4.365 @@ -544,7 +652,7 @@
   4.366          for (Option o : recognizedOptions) {
   4.367              String name = o.aliases[0].substring(1); // there must always be at least one name
   4.368              name = name.charAt(0) == '-' ? name.substring(1) : name;
   4.369 -            if (o.isHidden() || name.equals("h")) {
   4.370 +            if (o.isHidden() || name.equals("h") || name.startsWith("filter:")) {
   4.371                  continue;
   4.372              }
   4.373              log.println(getMessage("main.opt." + name));
   4.374 @@ -582,14 +690,18 @@
   4.375          boolean fullVersion;
   4.376          boolean showProfile;
   4.377          boolean showSummary;
   4.378 -        boolean wildcard;
   4.379          boolean apiOnly;
   4.380          boolean showLabel;
   4.381          boolean findJDKInternals;
   4.382 +        // default is to show package-level dependencies
   4.383 +        // and filter references from same package
   4.384 +        Analyzer.Type verbose = PACKAGE;
   4.385 +        boolean filterSamePackage = true;
   4.386 +        boolean filterSameArchive = false;
   4.387 +        String filterRegex;
   4.388          String dotOutputDir;
   4.389          String classpath = "";
   4.390          int depth = 1;
   4.391 -        Analyzer.Type verbose = Analyzer.Type.PACKAGE;
   4.392          Set<String> packageNames = new HashSet<>();
   4.393          String regex;             // apply to the dependences
   4.394          Pattern includePattern;   // apply to classes
   4.395 @@ -613,19 +725,6 @@
   4.396          }
   4.397      }
   4.398  
   4.399 -    private List<Archive> getArchives(List<String> filenames) throws IOException {
   4.400 -        List<Archive> result = new ArrayList<Archive>();
   4.401 -        for (String s : filenames) {
   4.402 -            Path p = Paths.get(s);
   4.403 -            if (Files.exists(p)) {
   4.404 -                result.add(new Archive(p, ClassFileReader.newInstance(p)));
   4.405 -            } else {
   4.406 -                warning("warn.file.not.exist", s);
   4.407 -            }
   4.408 -        }
   4.409 -        return result;
   4.410 -    }
   4.411 -
   4.412      private List<Archive> getClassPathArchives(String paths) throws IOException {
   4.413          List<Archive> result = new ArrayList<>();
   4.414          if (paths.isEmpty()) {
   4.415 @@ -648,7 +747,7 @@
   4.416                  }
   4.417                  for (Path f : files) {
   4.418                      if (Files.exists(f)) {
   4.419 -                        result.add(new Archive(f, ClassFileReader.newInstance(f)));
   4.420 +                        result.add(Archive.getInstance(f));
   4.421                      }
   4.422                  }
   4.423              }
   4.424 @@ -656,81 +755,50 @@
   4.425          return result;
   4.426      }
   4.427  
   4.428 -    /**
   4.429 -     * If the given archive is JDK archive and non-null Profile,
   4.430 -     * this method returns the profile name only if -profile option is specified;
   4.431 -     * a null profile indicates it accesses a private JDK API and this method
   4.432 -     * will return "JDK internal API".
   4.433 -     *
   4.434 -     * For non-JDK archives, this method returns the file name of the archive.
   4.435 -     */
   4.436 -    private String getProfileArchiveInfo(Archive source, Profile profile) {
   4.437 -        if (options.showProfile && profile != null)
   4.438 -            return profile.toString();
   4.439 -
   4.440 -        if (source instanceof JDKArchive) {
   4.441 -            return profile == null ? "JDK internal API (" + source.getFileName() + ")" : "";
   4.442 -        }
   4.443 -        return source.getFileName();
   4.444 -    }
   4.445 -
   4.446 -    /**
   4.447 -     * Returns the profile name or "JDK internal API" for JDK archive;
   4.448 -     * otherwise empty string.
   4.449 -     */
   4.450 -    private String profileName(Archive archive, Profile profile) {
   4.451 -        if (archive instanceof JDKArchive) {
   4.452 -            return Objects.toString(profile, "JDK internal API");
   4.453 -        } else {
   4.454 -            return "";
   4.455 -        }
   4.456 -    }
   4.457 -
   4.458      class RawOutputFormatter implements Analyzer.Visitor {
   4.459          private final PrintWriter writer;
   4.460 +        private String pkg = "";
   4.461          RawOutputFormatter(PrintWriter writer) {
   4.462              this.writer = writer;
   4.463          }
   4.464 -
   4.465 -        private String pkg = "";
   4.466          @Override
   4.467 -        public void visitDependence(String origin, Archive source,
   4.468 -                                    String target, Archive archive, Profile profile) {
   4.469 -            if (options.findJDKInternals &&
   4.470 -                    !(archive instanceof JDKArchive && profile == null)) {
   4.471 -                // filter dependences other than JDK internal APIs
   4.472 -                return;
   4.473 -            }
   4.474 -            if (options.verbose == Analyzer.Type.VERBOSE) {
   4.475 -                writer.format("   %-50s -> %-50s %s%n",
   4.476 -                              origin, target, getProfileArchiveInfo(archive, profile));
   4.477 +        public void visitDependence(String origin, Archive originArchive,
   4.478 +                                    String target, Archive targetArchive) {
   4.479 +            String tag = toTag(target, targetArchive);
   4.480 +            if (options.verbose == VERBOSE) {
   4.481 +                writer.format("   %-50s -> %-50s %s%n", origin, target, tag);
   4.482              } else {
   4.483                  if (!origin.equals(pkg)) {
   4.484                      pkg = origin;
   4.485 -                    writer.format("   %s (%s)%n", origin, source.getFileName());
   4.486 +                    writer.format("   %s (%s)%n", origin, originArchive.getName());
   4.487                  }
   4.488 -                writer.format("      -> %-50s %s%n",
   4.489 -                              target, getProfileArchiveInfo(archive, profile));
   4.490 -            }
   4.491 -        }
   4.492 -
   4.493 -        @Override
   4.494 -        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
   4.495 -            writer.format("%s -> %s", origin.getPathName(), target.getPathName());
   4.496 -            if (options.showProfile && profile != null) {
   4.497 -                writer.format(" (%s)%n", profile);
   4.498 -            } else {
   4.499 -                writer.format("%n");
   4.500 +                writer.format("      -> %-50s %s%n", target, tag);
   4.501              }
   4.502          }
   4.503      }
   4.504  
   4.505 -    class DotFileFormatter extends DotGraph<String> implements AutoCloseable {
   4.506 +    class RawSummaryFormatter implements Analyzer.Visitor {
   4.507 +        private final PrintWriter writer;
   4.508 +        RawSummaryFormatter(PrintWriter writer) {
   4.509 +            this.writer = writer;
   4.510 +        }
   4.511 +        @Override
   4.512 +        public void visitDependence(String origin, Archive originArchive,
   4.513 +                                    String target, Archive targetArchive) {
   4.514 +            writer.format("%s -> %s", originArchive.getName(), targetArchive.getPathName());
   4.515 +            if (options.showProfile && JDKArchive.isProfileArchive(targetArchive)) {
   4.516 +                writer.format(" (%s)", target);
   4.517 +            }
   4.518 +            writer.format("%n");
   4.519 +        }
   4.520 +    }
   4.521 +
   4.522 +    class DotFileFormatter implements Analyzer.Visitor, AutoCloseable {
   4.523          private final PrintWriter writer;
   4.524          private final String name;
   4.525          DotFileFormatter(PrintWriter writer, Archive archive) {
   4.526              this.writer = writer;
   4.527 -            this.name = archive.getFileName();
   4.528 +            this.name = archive.getName();
   4.529              writer.format("digraph \"%s\" {%n", name);
   4.530              writer.format("    // Path: %s%n", archive.getPathName());
   4.531          }
   4.532 @@ -741,173 +809,130 @@
   4.533          }
   4.534  
   4.535          @Override
   4.536 -        public void visitDependence(String origin, Archive source,
   4.537 -                                    String target, Archive archive, Profile profile) {
   4.538 -            if (options.findJDKInternals &&
   4.539 -                    !(archive instanceof JDKArchive && profile == null)) {
   4.540 -                // filter dependences other than JDK internal APIs
   4.541 -                return;
   4.542 -            }
   4.543 -            // if -P option is specified, package name -> profile will
   4.544 -            // be shown and filter out multiple same edges.
   4.545 -            String name = getProfileArchiveInfo(archive, profile);
   4.546 -            writeEdge(writer, new Edge(origin, target, getProfileArchiveInfo(archive, profile)));
   4.547 -        }
   4.548 -        @Override
   4.549 -        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
   4.550 -            throw new UnsupportedOperationException();
   4.551 +        public void visitDependence(String origin, Archive originArchive,
   4.552 +                                    String target, Archive targetArchive) {
   4.553 +            String tag = toTag(target, targetArchive);
   4.554 +            writer.format("   %-50s -> \"%s\";%n",
   4.555 +                          String.format("\"%s\"", origin),
   4.556 +                          tag.isEmpty() ? target
   4.557 +                                        : String.format("%s (%s)", target, tag));
   4.558          }
   4.559      }
   4.560  
   4.561 -    class DotSummaryForArchive extends DotGraph<Archive> {
   4.562 +    class SummaryDotFile implements Analyzer.Visitor, AutoCloseable {
   4.563 +        private final PrintWriter writer;
   4.564 +        private final Analyzer.Type type;
   4.565 +        private final Map<Archive, Map<Archive,StringBuilder>> edges = new HashMap<>();
   4.566 +        SummaryDotFile(PrintWriter writer, Analyzer.Type type) {
   4.567 +            this.writer = writer;
   4.568 +            this.type = type;
   4.569 +            writer.format("digraph \"summary\" {%n");
   4.570 +        }
   4.571 +
   4.572          @Override
   4.573 -        public void visitDependence(String origin, Archive source,
   4.574 -                                    String target, Archive archive, Profile profile) {
   4.575 -            Edge e = findEdge(source, archive);
   4.576 -            assert e != null;
   4.577 -            // add the dependency to the label if enabled and not compact1
   4.578 -            if (profile == Profile.COMPACT1) {
   4.579 -                return;
   4.580 +        public void close() {
   4.581 +            writer.println("}");
   4.582 +        }
   4.583 +
   4.584 +        @Override
   4.585 +        public void visitDependence(String origin, Archive originArchive,
   4.586 +                                    String target, Archive targetArchive) {
   4.587 +            String targetName = type == PACKAGE ? target : targetArchive.getName();
   4.588 +            if (type == PACKAGE) {
   4.589 +                String tag = toTag(target, targetArchive, type);
   4.590 +                if (!tag.isEmpty())
   4.591 +                    targetName += " (" + tag + ")";
   4.592 +            } else if (options.showProfile && JDKArchive.isProfileArchive(targetArchive)) {
   4.593 +                targetName += " (" + target + ")";
   4.594              }
   4.595 -            e.addLabel(origin, target, profileName(archive, profile));
   4.596 +            String label = getLabel(originArchive, targetArchive);
   4.597 +            writer.format("  %-50s -> \"%s\"%s;%n",
   4.598 +                          String.format("\"%s\"", origin), targetName, label);
   4.599          }
   4.600 -        @Override
   4.601 -        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
   4.602 -            // add an edge with the archive's name with no tag
   4.603 -            // so that there is only one node for each JDK archive
   4.604 -            // while there may be edges to different profiles
   4.605 -            Edge e = addEdge(origin, target, "");
   4.606 -            if (target instanceof JDKArchive) {
   4.607 -                // add a label to print the profile
   4.608 -                if (profile == null) {
   4.609 -                    e.addLabel("JDK internal API");
   4.610 -                } else if (options.showProfile && !options.showLabel) {
   4.611 -                    e.addLabel(profile.toString());
   4.612 +
   4.613 +        String getLabel(Archive origin, Archive target) {
   4.614 +            if (edges.isEmpty())
   4.615 +                return "";
   4.616 +
   4.617 +            StringBuilder label = edges.get(origin).get(target);
   4.618 +            return label == null ? "" : String.format(" [label=\"%s\",fontsize=9]", label.toString());
   4.619 +        }
   4.620 +
   4.621 +        Analyzer.Visitor labelBuilder() {
   4.622 +            // show the package-level dependencies as labels in the dot graph
   4.623 +            return new Analyzer.Visitor() {
   4.624 +                @Override
   4.625 +                public void visitDependence(String origin, Archive originArchive,
   4.626 +                                            String target, Archive targetArchive)
   4.627 +                {
   4.628 +                    Map<Archive,StringBuilder> labels = edges.get(originArchive);
   4.629 +                    if (!edges.containsKey(originArchive)) {
   4.630 +                        edges.put(originArchive, labels = new HashMap<>());
   4.631 +                    }
   4.632 +                    StringBuilder sb = labels.get(targetArchive);
   4.633 +                    if (sb == null) {
   4.634 +                        labels.put(targetArchive, sb = new StringBuilder());
   4.635 +                    }
   4.636 +                    String tag = toTag(target, targetArchive, PACKAGE);
   4.637 +                    addLabel(sb, origin, target, tag);
   4.638                  }
   4.639 -            }
   4.640 +
   4.641 +                void addLabel(StringBuilder label, String origin, String target, String tag) {
   4.642 +                    label.append(origin).append(" -> ").append(target);
   4.643 +                    if (!tag.isEmpty()) {
   4.644 +                        label.append(" (" + tag + ")");
   4.645 +                    }
   4.646 +                    label.append("\\n");
   4.647 +                }
   4.648 +            };
   4.649          }
   4.650      }
   4.651  
   4.652 -    // DotSummaryForPackage generates the summary.dot file for verbose mode
   4.653 -    // (-v or -verbose option) that includes all class dependencies.
   4.654 -    // The summary.dot file shows package-level dependencies.
   4.655 -    class DotSummaryForPackage extends DotGraph<String> {
   4.656 -        private String packageOf(String cn) {
   4.657 -            int i = cn.lastIndexOf('.');
   4.658 -            return i > 0 ? cn.substring(0, i) : "<unnamed>";
   4.659 +    /**
   4.660 +     * Test if the given archive is part of the JDK
   4.661 +     */
   4.662 +    private boolean isJDKArchive(Archive archive) {
   4.663 +        return JDKArchive.class.isInstance(archive);
   4.664 +    }
   4.665 +
   4.666 +    /**
   4.667 +     * If the given archive is JDK archive, this method returns the profile name
   4.668 +     * only if -profile option is specified; it accesses a private JDK API and
   4.669 +     * the returned value will have "JDK internal API" prefix
   4.670 +     *
   4.671 +     * For non-JDK archives, this method returns the file name of the archive.
   4.672 +     */
   4.673 +    private String toTag(String name, Archive source, Analyzer.Type type) {
   4.674 +        if (!isJDKArchive(source)) {
   4.675 +            return source.getName();
   4.676          }
   4.677 -        @Override
   4.678 -        public void visitDependence(String origin, Archive source,
   4.679 -                                    String target, Archive archive, Profile profile) {
   4.680 -            // add a package dependency edge
   4.681 -            String from = packageOf(origin);
   4.682 -            String to = packageOf(target);
   4.683 -            Edge e = addEdge(from, to, getProfileArchiveInfo(archive, profile));
   4.684  
   4.685 -            // add the dependency to the label if enabled and not compact1
   4.686 -            if (!options.showLabel || profile == Profile.COMPACT1) {
   4.687 -                return;
   4.688 -            }
   4.689 -
   4.690 -            // trim the package name of origin to shorten the label
   4.691 -            int i = origin.lastIndexOf('.');
   4.692 -            String n1 = i < 0 ? origin : origin.substring(i+1);
   4.693 -            e.addLabel(n1, target, profileName(archive, profile));
   4.694 +        JDKArchive jdk = (JDKArchive)source;
   4.695 +        boolean isExported = false;
   4.696 +        if (type == CLASS || type == VERBOSE) {
   4.697 +            isExported = jdk.isExported(name);
   4.698 +        } else {
   4.699 +            isExported = jdk.isExportedPackage(name);
   4.700          }
   4.701 -        @Override
   4.702 -        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
   4.703 -            // nop
   4.704 +        Profile p = getProfile(name, type);
   4.705 +        if (isExported) {
   4.706 +            // exported API
   4.707 +            return options.showProfile && p != null ? p.profileName() : "";
   4.708 +        } else {
   4.709 +            return "JDK internal API (" + source.getName() + ")";
   4.710          }
   4.711      }
   4.712 -    abstract class DotGraph<T> implements Analyzer.Visitor  {
   4.713 -        private final Set<Edge> edges = new LinkedHashSet<>();
   4.714 -        private Edge curEdge;
   4.715 -        public void writeTo(PrintWriter writer) {
   4.716 -            writer.format("digraph \"summary\" {%n");
   4.717 -            for (Edge e: edges) {
   4.718 -                writeEdge(writer, e);
   4.719 -            }
   4.720 -            writer.println("}");
   4.721 +
   4.722 +    private String toTag(String name, Archive source) {
   4.723 +        return toTag(name, source, options.verbose);
   4.724 +    }
   4.725 +
   4.726 +    private Profile getProfile(String name, Analyzer.Type type) {
   4.727 +        String pn = name;
   4.728 +        if (type == CLASS || type == VERBOSE) {
   4.729 +            int i = name.lastIndexOf('.');
   4.730 +            pn = i > 0 ? name.substring(0, i) : "";
   4.731          }
   4.732 -
   4.733 -        void writeEdge(PrintWriter writer, Edge e) {
   4.734 -            writer.format("   %-50s -> \"%s\"%s;%n",
   4.735 -                          String.format("\"%s\"", e.from.toString()),
   4.736 -                          e.tag.isEmpty() ? e.to
   4.737 -                                          : String.format("%s (%s)", e.to, e.tag),
   4.738 -                          getLabel(e));
   4.739 -        }
   4.740 -
   4.741 -        Edge addEdge(T origin, T target, String tag) {
   4.742 -            Edge e = new Edge(origin, target, tag);
   4.743 -            if (e.equals(curEdge)) {
   4.744 -                return curEdge;
   4.745 -            }
   4.746 -
   4.747 -            if (edges.contains(e)) {
   4.748 -                for (Edge e1 : edges) {
   4.749 -                   if (e.equals(e1)) {
   4.750 -                       curEdge = e1;
   4.751 -                   }
   4.752 -                }
   4.753 -            } else {
   4.754 -                edges.add(e);
   4.755 -                curEdge = e;
   4.756 -            }
   4.757 -            return curEdge;
   4.758 -        }
   4.759 -
   4.760 -        Edge findEdge(T origin, T target) {
   4.761 -            for (Edge e : edges) {
   4.762 -                if (e.from.equals(origin) && e.to.equals(target)) {
   4.763 -                    return e;
   4.764 -                }
   4.765 -            }
   4.766 -            return null;
   4.767 -        }
   4.768 -
   4.769 -        String getLabel(Edge e) {
   4.770 -            String label = e.label.toString();
   4.771 -            return label.isEmpty() ? "" : String.format("[label=\"%s\",fontsize=9]", label);
   4.772 -        }
   4.773 -
   4.774 -        class Edge {
   4.775 -            final T from;
   4.776 -            final T to;
   4.777 -            final String tag;  // optional tag
   4.778 -            final StringBuilder label = new StringBuilder();
   4.779 -            Edge(T from, T to, String tag) {
   4.780 -                this.from = from;
   4.781 -                this.to = to;
   4.782 -                this.tag = tag;
   4.783 -            }
   4.784 -            void addLabel(String s) {
   4.785 -                label.append(s).append("\\n");
   4.786 -            }
   4.787 -            void addLabel(String origin, String target, String profile) {
   4.788 -                label.append(origin).append(" -> ").append(target);
   4.789 -                if (!profile.isEmpty()) {
   4.790 -                    label.append(" (" + profile + ")");
   4.791 -                }
   4.792 -                label.append("\\n");
   4.793 -            }
   4.794 -            @Override @SuppressWarnings("unchecked")
   4.795 -            public boolean equals(Object o) {
   4.796 -                if (o instanceof DotGraph<?>.Edge) {
   4.797 -                    DotGraph<?>.Edge e = (DotGraph<?>.Edge)o;
   4.798 -                    return this.from.equals(e.from) &&
   4.799 -                           this.to.equals(e.to) &&
   4.800 -                           this.tag.equals(e.tag);
   4.801 -                }
   4.802 -                return false;
   4.803 -            }
   4.804 -            @Override
   4.805 -            public int hashCode() {
   4.806 -                int hash = 7;
   4.807 -                hash = 67 * hash + Objects.hashCode(this.from) +
   4.808 -                       Objects.hashCode(this.to) + Objects.hashCode(this.tag);
   4.809 -                return hash;
   4.810 -            }
   4.811 -        }
   4.812 +        return Profile.getProfile(pn);
   4.813      }
   4.814  }
     5.1 --- a/src/share/classes/com/sun/tools/jdeps/Main.java	Mon Jul 07 18:03:08 2014 -0700
     5.2 +++ b/src/share/classes/com/sun/tools/jdeps/Main.java	Thu Jul 17 15:23:08 2014 -0700
     5.3 @@ -1,5 +1,5 @@
     5.4  /*
     5.5 - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
     5.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
     5.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     5.8   *
     5.9   * This code is free software; you can redistribute it and/or modify it
    5.10 @@ -63,4 +63,3 @@
    5.11          return t.run(args);
    5.12      }
    5.13  }
    5.14 -
     6.1 --- a/src/share/classes/com/sun/tools/jdeps/PlatformClassPath.java	Mon Jul 07 18:03:08 2014 -0700
     6.2 +++ b/src/share/classes/com/sun/tools/jdeps/PlatformClassPath.java	Thu Jul 17 15:23:08 2014 -0700
     6.3 @@ -1,5 +1,5 @@
     6.4  /*
     6.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     6.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
     6.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     6.8   *
     6.9   * This code is free software; you can redistribute it and/or modify it
    6.10 @@ -24,6 +24,12 @@
    6.11   */
    6.12  package com.sun.tools.jdeps;
    6.13  
    6.14 +import com.sun.tools.classfile.Annotation;
    6.15 +import com.sun.tools.classfile.ClassFile;
    6.16 +import com.sun.tools.classfile.ConstantPool;
    6.17 +import com.sun.tools.classfile.ConstantPoolException;
    6.18 +import com.sun.tools.classfile.RuntimeAnnotations_attribute;
    6.19 +import com.sun.tools.classfile.Dependencies.ClassFileError;
    6.20  import java.io.IOException;
    6.21  import java.nio.file.FileVisitResult;
    6.22  import java.nio.file.Files;
    6.23 @@ -33,11 +39,15 @@
    6.24  import java.nio.file.attribute.BasicFileAttributes;
    6.25  import java.util.*;
    6.26  
    6.27 +import static com.sun.tools.classfile.Attribute.*;
    6.28 +
    6.29  /**
    6.30   * ClassPath for Java SE and JDK
    6.31   */
    6.32  class PlatformClassPath {
    6.33 -    private final static List<Archive> javaHomeArchives = init();
    6.34 +    private static final List<String> NON_PLATFORM_JARFILES =
    6.35 +        Arrays.asList("alt-rt.jar", "jfxrt.jar", "ant-javafx.jar", "javafx-mx.jar");
    6.36 +    private static final List<Archive> javaHomeArchives = init();
    6.37  
    6.38      static List<Archive> getArchives() {
    6.39          return javaHomeArchives;
    6.40 @@ -50,12 +60,19 @@
    6.41              if (home.endsWith("jre")) {
    6.42                  // jar files in <javahome>/jre/lib
    6.43                  result.addAll(addJarFiles(home.resolve("lib")));
    6.44 +                if (home.getParent() != null) {
    6.45 +                    // add tools.jar and other JDK jar files
    6.46 +                    Path lib = home.getParent().resolve("lib");
    6.47 +                    if (Files.exists(lib)) {
    6.48 +                        result.addAll(addJarFiles(lib));
    6.49 +                    }
    6.50 +                }
    6.51              } else if (Files.exists(home.resolve("lib"))) {
    6.52                  // either a JRE or a jdk build image
    6.53                  Path classes = home.resolve("classes");
    6.54                  if (Files.isDirectory(classes)) {
    6.55                      // jdk build outputdir
    6.56 -                    result.add(new JDKArchive(classes, ClassFileReader.newInstance(classes)));
    6.57 +                    result.add(new JDKArchive(classes));
    6.58                  }
    6.59                  // add other JAR files
    6.60                  result.addAll(addJarFiles(home.resolve("lib")));
    6.61 @@ -91,9 +108,9 @@
    6.62                  if (fn.endsWith(".jar")) {
    6.63                      // JDK may cobundle with JavaFX that doesn't belong to any profile
    6.64                      // Treat jfxrt.jar as regular Archive
    6.65 -                    result.add(fn.equals("jfxrt.jar")
    6.66 -                        ? new Archive(p, ClassFileReader.newInstance(p))
    6.67 -                        : new JDKArchive(p, ClassFileReader.newInstance(p)));
    6.68 +                    result.add(NON_PLATFORM_JARFILES.contains(fn)
    6.69 +                                   ? Archive.getInstance(p)
    6.70 +                                   : new JDKArchive(p));
    6.71                  }
    6.72                  return FileVisitResult.CONTINUE;
    6.73              }
    6.74 @@ -106,8 +123,91 @@
    6.75       * or implementation classes (i.e. JDK internal API)
    6.76       */
    6.77      static class JDKArchive extends Archive {
    6.78 -        JDKArchive(Path p, ClassFileReader reader) {
    6.79 -            super(p, reader);
    6.80 +        private static List<String> PROFILE_JARS = Arrays.asList("rt.jar", "jce.jar");
    6.81 +        public static boolean isProfileArchive(Archive archive) {
    6.82 +            if (archive instanceof JDKArchive) {
    6.83 +                return PROFILE_JARS.contains(archive.getName());
    6.84 +            }
    6.85 +            return false;
    6.86 +        }
    6.87 +
    6.88 +        private final Map<String,Boolean> exportedPackages = new HashMap<>();
    6.89 +        private final Map<String,Boolean> exportedTypes = new HashMap<>();
    6.90 +        JDKArchive(Path p) throws IOException {
    6.91 +            super(p, ClassFileReader.newInstance(p));
    6.92 +        }
    6.93 +
    6.94 +        /**
    6.95 +         * Tests if a given fully-qualified name is an exported type.
    6.96 +         */
    6.97 +        public boolean isExported(String cn) {
    6.98 +            int i = cn.lastIndexOf('.');
    6.99 +            String pn = i > 0 ? cn.substring(0, i) : "";
   6.100 +
   6.101 +            boolean isJdkExported = isExportedPackage(pn);
   6.102 +            if (exportedTypes.containsKey(cn)) {
   6.103 +                return exportedTypes.get(cn);
   6.104 +            }
   6.105 +            return isJdkExported;
   6.106 +        }
   6.107 +
   6.108 +        /**
   6.109 +         * Tests if a given package name is exported.
   6.110 +         */
   6.111 +        public boolean isExportedPackage(String pn) {
   6.112 +            if (Profile.getProfile(pn) != null) {
   6.113 +                return true;
   6.114 +            }
   6.115 +            return exportedPackages.containsKey(pn) ? exportedPackages.get(pn) : false;
   6.116 +        }
   6.117 +
   6.118 +        private static final String JDK_EXPORTED_ANNOTATION = "Ljdk/Exported;";
   6.119 +        private Boolean isJdkExported(ClassFile cf) throws ConstantPoolException {
   6.120 +            RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute)
   6.121 +                    cf.attributes.get(RuntimeVisibleAnnotations);
   6.122 +            if (attr != null) {
   6.123 +                for (int i = 0; i < attr.annotations.length; i++) {
   6.124 +                    Annotation ann = attr.annotations[i];
   6.125 +                    String annType = cf.constant_pool.getUTF8Value(ann.type_index);
   6.126 +                    if (JDK_EXPORTED_ANNOTATION.equals(annType)) {
   6.127 +                        boolean isJdkExported = true;
   6.128 +                        for (int j = 0; j < ann.num_element_value_pairs; j++) {
   6.129 +                            Annotation.element_value_pair pair = ann.element_value_pairs[j];
   6.130 +                            Annotation.Primitive_element_value ev = (Annotation.Primitive_element_value) pair.value;
   6.131 +                            ConstantPool.CONSTANT_Integer_info info = (ConstantPool.CONSTANT_Integer_info)
   6.132 +                                    cf.constant_pool.get(ev.const_value_index);
   6.133 +                            isJdkExported = info.value != 0;
   6.134 +                        }
   6.135 +                        return Boolean.valueOf(isJdkExported);
   6.136 +                    }
   6.137 +                }
   6.138 +            }
   6.139 +            return null;
   6.140 +        }
   6.141 +
   6.142 +        void processJdkExported(ClassFile cf) throws IOException {
   6.143 +            try {
   6.144 +                String cn = cf.getName();
   6.145 +                String pn = cn.substring(0, cn.lastIndexOf('/')).replace('/', '.');
   6.146 +
   6.147 +                Boolean b = isJdkExported(cf);
   6.148 +                if (b != null) {
   6.149 +                    exportedTypes.put(cn.replace('/', '.'), b);
   6.150 +                }
   6.151 +                if (!exportedPackages.containsKey(pn)) {
   6.152 +                    // check if package-info.class has @jdk.Exported
   6.153 +                    Boolean isJdkExported = null;
   6.154 +                    ClassFile pcf = reader().getClassFile(cn.substring(0, cn.lastIndexOf('/')+1) + "package-info");
   6.155 +                    if (pcf != null) {
   6.156 +                        isJdkExported = isJdkExported(pcf);
   6.157 +                    }
   6.158 +                    if (isJdkExported != null) {
   6.159 +                        exportedPackages.put(pn, isJdkExported);
   6.160 +                    }
   6.161 +                }
   6.162 +            } catch (ConstantPoolException e) {
   6.163 +                throw new ClassFileError(e);
   6.164 +            }
   6.165          }
   6.166      }
   6.167  }
     7.1 --- a/src/share/classes/com/sun/tools/jdeps/Profile.java	Mon Jul 07 18:03:08 2014 -0700
     7.2 +++ b/src/share/classes/com/sun/tools/jdeps/Profile.java	Thu Jul 17 15:23:08 2014 -0700
     7.3 @@ -1,5 +1,5 @@
     7.4  /*
     7.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
     7.6 + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
     7.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.8   *
     7.9   * This code is free software; you can redistribute it and/or modify it
    7.10 @@ -43,7 +43,6 @@
    7.11   * Build the profile information from ct.sym if exists.
    7.12   */
    7.13  enum Profile {
    7.14 -
    7.15      COMPACT1("compact1", 1),
    7.16      COMPACT2("compact2", 2),
    7.17      COMPACT3("compact3", 3),
    7.18 @@ -61,8 +60,7 @@
    7.19          this.proprietaryPkgs = new HashSet<>();
    7.20      }
    7.21  
    7.22 -    @Override
    7.23 -    public String toString() {
    7.24 +    public String profileName() {
    7.25          return name;
    7.26      }
    7.27  
    7.28 @@ -77,7 +75,7 @@
    7.29      public static Profile getProfile(String pn) {
    7.30          Profile profile = PackageToProfile.map.get(pn);
    7.31          return (profile != null && profile.packages.contains(pn))
    7.32 -                ? profile : null;
    7.33 +                    ? profile : null;
    7.34      }
    7.35  
    7.36      static class PackageToProfile {
     8.1 --- a/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Mon Jul 07 18:03:08 2014 -0700
     8.2 +++ b/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Thu Jul 17 15:23:08 2014 -0700
     8.3 @@ -1,6 +1,6 @@
     8.4  main.usage.summary=\
     8.5  Usage: {0} <options> <classes...>\n\
     8.6 -use -h, -? or --help for a list of possible options
     8.7 +use -h, -? or -help for a list of possible options
     8.8  
     8.9  main.usage=\
    8.10  Usage: {0} <options> <classes...>\n\
    8.11 @@ -18,20 +18,29 @@
    8.12  
    8.13  main.opt.v=\
    8.14  \  -v           -verbose              Print all class level dependencies\n\
    8.15 +\                                     Equivalent to -verbose:class -filter:none.\n\
    8.16  \  -verbose:package                   Print package-level dependencies excluding\n\
    8.17 -\                                     dependencies within the same archive\n\
    8.18 +\                                     dependencies within the same package by default\n\
    8.19  \  -verbose:class                     Print class-level dependencies excluding\n\
    8.20 -\                                     dependencies within the same archive
    8.21 +\                                     dependencies within the same package by default
    8.22 +
    8.23 +main.opt.f=\
    8.24 +\  -f <regex>   -filter <regex>       Filter dependences matching the given pattern\n\
    8.25 +\                                     If given multiple times, the last one will be used.\n\
    8.26 +\  -filter:package                    Filter dependences within the same package (default)\n\
    8.27 +\  -filter:archive                    Filter dependences within the same archive\n\
    8.28 +\  -filter:none                       No -filter:package and -filter:archive filtering\n\
    8.29 +\                                     Filtering specified via the -filter option still applies.
    8.30  
    8.31  main.opt.s=\
    8.32  \  -s           -summary              Print dependency summary only
    8.33  
    8.34  main.opt.p=\
    8.35 -\  -p <pkgname> -package <pkgname>    Finds dependences in the given package\n\
    8.36 +\  -p <pkgname> -package <pkgname>    Finds dependences matching the given package name\n\
    8.37  \                                     (may be given multiple times)
    8.38  
    8.39  main.opt.e=\
    8.40 -\  -e <regex>   -regex <regex>        Finds dependences in packages matching pattern\n\
    8.41 +\  -e <regex>   -regex <regex>        Finds dependences matching the given pattern\n\
    8.42  \                                     (-p and -e are exclusive)
    8.43  
    8.44  main.opt.include=\
    8.45 @@ -47,7 +56,10 @@
    8.46  \  -cp <path>   -classpath <path>     Specify where to find class files
    8.47  
    8.48  main.opt.R=\
    8.49 -\  -R           -recursive            Recursively traverse all dependencies
    8.50 +\  -R           -recursive            Recursively traverse all dependencies.\n\
    8.51 +\                                     The -R option implies -filter:none.  If -p, -e, -f\n\
    8.52 +\                                     option is specified, only the matching dependences\n\
    8.53 +\                                     are analyzed.
    8.54  
    8.55  main.opt.apionly=\
    8.56  \  -apionly                           Restrict analysis to APIs i.e. dependences\n\
    8.57 @@ -74,12 +86,11 @@
    8.58  
    8.59  err.unknown.option=unknown option: {0}
    8.60  err.missing.arg=no value given for {0}
    8.61 -err.internal.error=internal error: {0} {1} {2}
    8.62  err.invalid.arg.for.option=invalid argument for option: {0}
    8.63  err.option.after.class=option must be specified before classes: {0}
    8.64  err.option.unsupported={0} not supported: {1}
    8.65  err.profiles.msg=No profile information
    8.66 -err.dot.output.path=invalid path: {0}
    8.67 +err.invalid.path=invalid path: {0}
    8.68  warn.invalid.arg=Invalid classname or pathname not exist: {0}
    8.69  warn.split.package=package {0} defined in {1} {2}
    8.70  
     9.1 --- a/test/tools/jdeps/APIDeps.java	Mon Jul 07 18:03:08 2014 -0700
     9.2 +++ b/test/tools/jdeps/APIDeps.java	Thu Jul 17 15:23:08 2014 -0700
     9.3 @@ -1,5 +1,5 @@
     9.4  /*
     9.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     9.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
     9.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     9.8   *
     9.9   * This code is free software; you can redistribute it and/or modify it
    9.10 @@ -23,7 +23,7 @@
    9.11  
    9.12  /*
    9.13   * @test
    9.14 - * @bug 8015912 8029216
    9.15 + * @bug 8015912 8029216 8048063
    9.16   * @summary Test -apionly and -jdkinternals options
    9.17   * @build m.Bar m.Foo m.Gee b.B c.C c.I d.D e.E f.F g.G
    9.18   * @run main APIDeps
    9.19 @@ -81,27 +81,39 @@
    9.20               new String[] {"compact1", "compact3", testDirBasename},
    9.21               new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"});
    9.22          test(new File(mDir, "Foo.class"),
    9.23 +             new String[] {"c.I", "e.E", "f.F"},
    9.24 +             new String[] {testDirBasename},
    9.25 +             new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-P"});
    9.26 +        test(new File(mDir, "Foo.class"),
    9.27               new String[] {"c.I", "e.E", "f.F", "m.Bar"},
    9.28               new String[] {testDirBasename},
    9.29 +             new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-filter:none", "-P"});
    9.30 +        test(new File(mDir, "Gee.class"),
    9.31 +             new String[] {"g.G", "sun.misc.Lock", "com.sun.tools.classfile.ClassFile",
    9.32 +                           "com.sun.management.ThreadMXBean", "com.sun.source.tree.BinaryTree"},
    9.33 +             new String[] {testDirBasename, "JDK internal API", "compact3", ""},
    9.34               new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"});
    9.35 -        test(new File(mDir, "Gee.class"),
    9.36 -             new String[] {"g.G", "sun.misc.Lock"},
    9.37 -             new String[] {testDirBasename, "JDK internal API"},
    9.38 -             new String[] {"-classpath", testDir.getPath(), "-verbose"});
    9.39  
    9.40          // -jdkinternals
    9.41          test(new File(mDir, "Gee.class"),
    9.42 -             new String[] {"sun.misc.Lock"},
    9.43 +             new String[] {"sun.misc.Lock", "com.sun.tools.classfile.ClassFile"},
    9.44               new String[] {"JDK internal API"},
    9.45               new String[] {"-jdkinternals"});
    9.46          // -jdkinternals parses all classes on -classpath and the input arguments
    9.47          test(new File(mDir, "Gee.class"),
    9.48 -             new String[] {"sun.misc.Lock", "sun.misc.Unsafe"},
    9.49 +             new String[] {"com.sun.tools.jdeps.Main", "com.sun.tools.classfile.ClassFile",
    9.50 +                           "sun.misc.Lock", "sun.misc.Unsafe"},
    9.51               new String[] {"JDK internal API"},
    9.52               new String[] {"-classpath", testDir.getPath(), "-jdkinternals"});
    9.53  
    9.54          // parse only APIs
    9.55 -        // parse only APIs
    9.56 +        test(mDir,
    9.57 +             new String[] {"java.lang.Object", "java.lang.String",
    9.58 +                           "java.util.Set",
    9.59 +                           "c.C", "d.D", "c.I", "e.E"},
    9.60 +             new String[] {"compact1", testDirBasename},
    9.61 +             new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-P", "-apionly"});
    9.62 +
    9.63          test(mDir,
    9.64               new String[] {"java.lang.Object", "java.lang.String",
    9.65                             "java.util.Set",
    10.1 --- a/test/tools/jdeps/Basic.java	Mon Jul 07 18:03:08 2014 -0700
    10.2 +++ b/test/tools/jdeps/Basic.java	Thu Jul 17 15:23:08 2014 -0700
    10.3 @@ -1,5 +1,5 @@
    10.4  /*
    10.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
    10.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
    10.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    10.8   *
    10.9   * This code is free software; you can redistribute it and/or modify it
   10.10 @@ -23,7 +23,7 @@
   10.11  
   10.12  /*
   10.13   * @test
   10.14 - * @bug 8003562 8005428 8015912 8027481
   10.15 + * @bug 8003562 8005428 8015912 8027481 8048063
   10.16   * @summary Basic tests for jdeps tool
   10.17   * @build Test p.Foo p.Bar javax.activity.NotCompactProfile
   10.18   * @run main Basic
   10.19 @@ -86,6 +86,16 @@
   10.20               new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
   10.21               new String[] {"compact1", "compact1", "not found", "not found"},
   10.22               new String[] {"-verbose:class"});
   10.23 +        // test -filter:none option
   10.24 +        test(new File(testDir, "p"),
   10.25 +             new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto", "p"},
   10.26 +             new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1", "p"},
   10.27 +             new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:none"});
   10.28 +        // test -filter:archive option
   10.29 +        test(new File(testDir, "p"),
   10.30 +             new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"},
   10.31 +             new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
   10.32 +             new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:archive"});
   10.33          // test -p option
   10.34          test(new File(testDir, "Test.class"),
   10.35               new String[] {"p.Foo", "p.Bar"},
   10.36 @@ -100,11 +110,12 @@
   10.37               new String[] {"java.lang"},
   10.38               new String[] {"compact1"},
   10.39               new String[] {"-verbose:package", "-e", "java\\.lang\\..*"});
   10.40 +
   10.41          // test -classpath and -include options
   10.42          test(null,
   10.43 -             new String[] {"java.lang", "java.util",
   10.44 -                           "java.lang.management", "javax.crypto"},
   10.45 -             new String[] {"compact1", "compact1", "compact3", "compact1"},
   10.46 +             new String[] {"java.lang", "java.util", "java.lang.management",
   10.47 +                           "javax.activity", "javax.crypto"},
   10.48 +             new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
   10.49               new String[] {"-classpath", testDir.getPath(), "-include", "p.+|Test.class"});
   10.50          test(new File(testDir, "Test.class"),
   10.51               new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/test/tools/jdeps/DotFileTest.java	Thu Jul 17 15:23:08 2014 -0700
    11.3 @@ -0,0 +1,272 @@
    11.4 +/*
    11.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
    11.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    11.7 + *
    11.8 + * This code is free software; you can redistribute it and/or modify it
    11.9 + * under the terms of the GNU General Public License version 2 only, as
   11.10 + * published by the Free Software Foundation.
   11.11 + *
   11.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   11.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   11.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   11.15 + * version 2 for more details (a copy is included in the LICENSE file that
   11.16 + * accompanied this code).
   11.17 + *
   11.18 + * You should have received a copy of the GNU General Public License version
   11.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   11.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   11.21 + *
   11.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   11.23 + * or visit www.oracle.com if you need additional information or have any
   11.24 + * questions.
   11.25 + */
   11.26 +
   11.27 +/*
   11.28 + * @test
   11.29 + * @bug 8003562
   11.30 + * @summary Basic tests for jdeps -dotoutput option
   11.31 + * @build Test p.Foo p.Bar javax.activity.NotCompactProfile
   11.32 + * @run main DotFileTest
   11.33 + */
   11.34 +
   11.35 +import java.io.File;
   11.36 +import java.io.IOException;
   11.37 +import java.io.PrintWriter;
   11.38 +import java.io.StringWriter;
   11.39 +import java.nio.file.DirectoryStream;
   11.40 +import java.nio.file.Files;
   11.41 +import java.nio.file.Path;
   11.42 +import java.nio.file.Paths;
   11.43 +import java.util.*;
   11.44 +import java.util.regex.*;
   11.45 +
   11.46 +public class DotFileTest {
   11.47 +    private static boolean symbolFileExist = initProfiles();
   11.48 +    private static boolean initProfiles() {
   11.49 +        // check if ct.sym exists; if not use the profiles.properties file
   11.50 +        Path home = Paths.get(System.getProperty("java.home"));
   11.51 +        if (home.endsWith("jre")) {
   11.52 +            home = home.getParent();
   11.53 +        }
   11.54 +        Path ctsym = home.resolve("lib").resolve("ct.sym");
   11.55 +        boolean symbolExists = ctsym.toFile().exists();
   11.56 +        if (!symbolExists) {
   11.57 +            Path testSrcProfiles =
   11.58 +                Paths.get(System.getProperty("test.src", "."), "profiles.properties");
   11.59 +            if (!testSrcProfiles.toFile().exists())
   11.60 +                throw new Error(testSrcProfiles + " does not exist");
   11.61 +            System.out.format("%s doesn't exist.%nUse %s to initialize profiles info%n",
   11.62 +                ctsym, testSrcProfiles);
   11.63 +            System.setProperty("jdeps.profiles", testSrcProfiles.toString());
   11.64 +        }
   11.65 +        return symbolExists;
   11.66 +    }
   11.67 +
   11.68 +    public static void main(String... args) throws Exception {
   11.69 +        int errors = 0;
   11.70 +        errors += new DotFileTest().run();
   11.71 +        if (errors > 0)
   11.72 +            throw new Exception(errors + " errors found");
   11.73 +    }
   11.74 +
   11.75 +    final Path dir;
   11.76 +    final Path dotoutput;
   11.77 +    DotFileTest() {
   11.78 +        this.dir = Paths.get(System.getProperty("test.classes", "."));
   11.79 +        this.dotoutput = dir.resolve("dots");
   11.80 +    }
   11.81 +
   11.82 +    int run() throws IOException {
   11.83 +        File testDir = dir.toFile();
   11.84 +        // test a .class file
   11.85 +        test(new File(testDir, "Test.class"),
   11.86 +             new String[] {"java.lang", "p"},
   11.87 +             new String[] {"compact1", "not found"});
   11.88 +        // test a directory
   11.89 +        // also test non-SE javax.activity class dependency
   11.90 +        test(new File(testDir, "p"),
   11.91 +             new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"},
   11.92 +             new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
   11.93 +             new String[] {"-classpath", testDir.getPath()});
   11.94 +        // test class-level dependency output
   11.95 +        test(new File(testDir, "Test.class"),
   11.96 +             new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
   11.97 +             new String[] {"compact1", "compact1", "not found", "not found"},
   11.98 +             new String[] {"-verbose:class"});
   11.99 +        // test -filter:none option
  11.100 +        test(new File(testDir, "p"),
  11.101 +             new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto", "p"},
  11.102 +             new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1", "p"},
  11.103 +             new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:none"});
  11.104 +        // test -filter:archive option
  11.105 +        test(new File(testDir, "p"),
  11.106 +             new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"},
  11.107 +             new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
  11.108 +             new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:archive"});
  11.109 +        // test -p option
  11.110 +        test(new File(testDir, "Test.class"),
  11.111 +             new String[] {"p.Foo", "p.Bar"},
  11.112 +             new String[] {"not found", "not found"},
  11.113 +             new String[] {"-verbose:class", "-p", "p"});
  11.114 +        // test -e option
  11.115 +        test(new File(testDir, "Test.class"),
  11.116 +             new String[] {"p.Foo", "p.Bar"},
  11.117 +             new String[] {"not found", "not found"},
  11.118 +             new String[] {"-verbose:class", "-e", "p\\..*"});
  11.119 +        test(new File(testDir, "Test.class"),
  11.120 +             new String[] {"java.lang"},
  11.121 +             new String[] {"compact1"},
  11.122 +             new String[] {"-verbose:package", "-e", "java\\.lang\\..*"});
  11.123 +        // test -classpath options
  11.124 +        test(new File(testDir, "Test.class"),
  11.125 +             new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
  11.126 +             new String[] {"compact1", "compact1", testDir.getName(), testDir.getName()},
  11.127 +             new String[] {"-v", "-classpath", testDir.getPath()});
  11.128 +
  11.129 +        testSummary(new File(testDir, "Test.class"),
  11.130 +             new String[] {"rt.jar", testDir.getName()},
  11.131 +             new String[] {"compact1", ""},
  11.132 +             new String[] {"-classpath", testDir.getPath()});
  11.133 +        testSummary(new File(testDir, "Test.class"),
  11.134 +             new String[] {"java.lang", "p"},
  11.135 +             new String[] {"compact1", testDir.getName()},
  11.136 +             new String[] {"-v", "-classpath", testDir.getPath()});
  11.137 +        return errors;
  11.138 +    }
  11.139 +
  11.140 +    void test(File file, String[] expect, String[] profiles) throws IOException {
  11.141 +        test(file, expect, profiles, new String[0]);
  11.142 +    }
  11.143 +
  11.144 +    void test(File file, String[] expect, String[] profiles, String[] options)
  11.145 +        throws IOException
  11.146 +    {
  11.147 +        Path dotfile = dotoutput.resolve(file.toPath().getFileName().toString() + ".dot");
  11.148 +
  11.149 +        List<String> args = new ArrayList<>(Arrays.asList(options));
  11.150 +        args.add("-dotoutput");
  11.151 +        args.add(dotoutput.toString());
  11.152 +        if (file != null) {
  11.153 +            args.add(file.getPath());
  11.154 +        }
  11.155 +
  11.156 +        Map<String,String> result = jdeps(args, dotfile);
  11.157 +        checkResult("dependencies", expect, result.keySet());
  11.158 +
  11.159 +        // with -P option
  11.160 +        List<String> argsWithDashP = new ArrayList<>();
  11.161 +        argsWithDashP.add("-dotoutput");
  11.162 +        argsWithDashP.add(dotoutput.toString());
  11.163 +        argsWithDashP.add("-P");
  11.164 +        argsWithDashP.addAll(args);
  11.165 +
  11.166 +        result = jdeps(argsWithDashP, dotfile);
  11.167 +        checkResult("profiles", expect, profiles, result);
  11.168 +    }
  11.169 +
  11.170 +    void testSummary(File file, String[] expect, String[] profiles, String[] options)
  11.171 +        throws IOException
  11.172 +    {
  11.173 +        Path dotfile = dotoutput.resolve("summary.dot");
  11.174 +
  11.175 +        List<String> args = new ArrayList<>(Arrays.asList(options));
  11.176 +        args.add("-dotoutput");
  11.177 +        args.add(dotoutput.toString());
  11.178 +        if (file != null) {
  11.179 +            args.add(file.getPath());
  11.180 +        }
  11.181 +
  11.182 +        Map<String,String> result = jdeps(args, dotfile);
  11.183 +        checkResult("dependencies", expect, result.keySet());
  11.184 +
  11.185 +        // with -P option
  11.186 +        List<String> argsWithDashP = new ArrayList<>();
  11.187 +        argsWithDashP.add("-dotoutput");
  11.188 +        argsWithDashP.add(dotoutput.toString());
  11.189 +        argsWithDashP.add("-P");
  11.190 +        argsWithDashP.addAll(args);
  11.191 +
  11.192 +        result = jdeps(argsWithDashP, dotfile);
  11.193 +        checkResult("profiles", expect, profiles, result);
  11.194 +    }
  11.195 +
  11.196 +    Map<String,String> jdeps(List<String> args, Path dotfile) throws IOException {
  11.197 +        if (Files.exists(dotoutput)) {
  11.198 +            try (DirectoryStream<Path> stream = Files.newDirectoryStream(dotoutput)) {
  11.199 +                for (Path p : stream) {
  11.200 +                    Files.delete(p);
  11.201 +                }
  11.202 +            }
  11.203 +            Files.delete(dotoutput);
  11.204 +        }
  11.205 +        // invoke jdeps
  11.206 +        StringWriter sw = new StringWriter();
  11.207 +        PrintWriter pw = new PrintWriter(sw);
  11.208 +        System.err.println("jdeps " + args);
  11.209 +        int rc = com.sun.tools.jdeps.Main.run(args.toArray(new String[0]), pw);
  11.210 +        pw.close();
  11.211 +        String out = sw.toString();
  11.212 +        if (!out.isEmpty())
  11.213 +            System.err.println(out);
  11.214 +        if (rc != 0)
  11.215 +            throw new Error("jdeps failed: rc=" + rc);
  11.216 +
  11.217 +        // check output files
  11.218 +        if (Files.notExists(dotfile)) {
  11.219 +            throw new RuntimeException(dotfile + " doesn't exist");
  11.220 +        }
  11.221 +        return parse(dotfile);
  11.222 +    }
  11.223 +    private static Pattern pattern = Pattern.compile("(.*) -> +([^ ]*) (.*)");
  11.224 +    private Map<String,String> parse(Path outfile) throws IOException {
  11.225 +        Map<String,String> result = new LinkedHashMap<>();
  11.226 +        for (String line : Files.readAllLines(outfile)) {
  11.227 +            line = line.replace('"', ' ').replace(';', ' ');
  11.228 +            Matcher pm = pattern.matcher(line);
  11.229 +            if (pm.find()) {
  11.230 +                String origin = pm.group(1).trim();
  11.231 +                String target = pm.group(2).trim();
  11.232 +                String module = pm.group(3).replace('(', ' ').replace(')', ' ').trim();
  11.233 +                result.put(target, module);
  11.234 +            }
  11.235 +        }
  11.236 +        return result;
  11.237 +    }
  11.238 +
  11.239 +    void checkResult(String label, String[] expect, Collection<String> found) {
  11.240 +        List<String> list = Arrays.asList(expect);
  11.241 +        if (!isEqual(list, found))
  11.242 +            error("Unexpected " + label + " found: '" + found + "', expected: '" + list + "'");
  11.243 +    }
  11.244 +
  11.245 +    void checkResult(String label, String[] expect, String[] profiles, Map<String,String> result) {
  11.246 +        if (expect.length != profiles.length)
  11.247 +            error("Invalid expected names and profiles");
  11.248 +
  11.249 +        // check the dependencies
  11.250 +        checkResult(label, expect, result.keySet());
  11.251 +        // check profile information
  11.252 +        checkResult(label, profiles, result.values());
  11.253 +        for (int i=0; i < expect.length; i++) {
  11.254 +            String profile = result.get(expect[i]);
  11.255 +            if (!profile.equals(profiles[i]))
  11.256 +                error("Unexpected profile: '" + profile + "', expected: '" + profiles[i] + "'");
  11.257 +        }
  11.258 +    }
  11.259 +
  11.260 +    boolean isEqual(List<String> expected, Collection<String> found) {
  11.261 +        if (expected.size() != found.size())
  11.262 +            return false;
  11.263 +
  11.264 +        List<String> list = new ArrayList<>(found);
  11.265 +        list.removeAll(expected);
  11.266 +        return list.isEmpty();
  11.267 +    }
  11.268 +
  11.269 +    void error(String msg) {
  11.270 +        System.err.println("Error: " + msg);
  11.271 +        errors++;
  11.272 +    }
  11.273 +
  11.274 +    int errors;
  11.275 +}
    12.1 --- a/test/tools/jdeps/m/Gee.java	Mon Jul 07 18:03:08 2014 -0700
    12.2 +++ b/test/tools/jdeps/m/Gee.java	Thu Jul 17 15:23:08 2014 -0700
    12.3 @@ -1,5 +1,5 @@
    12.4  /*
    12.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
    12.6 + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
    12.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    12.8   *
    12.9   * This code is free software; you can redistribute it and/or modify it
   12.10 @@ -26,5 +26,7 @@
   12.11  
   12.12  class Gee extends g.G {
   12.13      public sun.misc.Lock lock;
   12.14 +    public com.sun.tools.classfile.ClassFile cf;     // @jdk.Exported(false)
   12.15 +    public com.sun.source.tree.BinaryTree tree;      // @jdk.Exported
   12.16 +    public com.sun.management.ThreadMXBean mxbean;   // @jdk.Exported on package-info
   12.17  }
   12.18 -
    13.1 --- a/test/tools/jdeps/p/Bar.java	Mon Jul 07 18:03:08 2014 -0700
    13.2 +++ b/test/tools/jdeps/p/Bar.java	Thu Jul 17 15:23:08 2014 -0700
    13.3 @@ -1,5 +1,5 @@
    13.4  /*
    13.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
    13.6 + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
    13.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    13.8   *
    13.9   * This code is free software; you can redistribute it and/or modify it
   13.10 @@ -30,4 +30,8 @@
   13.11      public javax.crypto.Cipher getCiper() {
   13.12          return null;
   13.13      }
   13.14 +
   13.15 +    public Foo foo() {
   13.16 +        return new Foo();
   13.17 +    }
   13.18  }

mercurial