src/share/classes/com/sun/tools/jdeps/Analyzer.java

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

author
mchung
date
Thu, 17 Jul 2014 15:23:08 -0700
changeset 2538
1e39ae45d8ac
parent 2172
aa91bc6e8480
child 2539
a51b7fd0543b
permissions
-rw-r--r--

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

     1 /*
     2  * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 package com.sun.tools.jdeps;
    27 import java.util.HashMap;
    28 import java.util.HashSet;
    29 import java.util.List;
    30 import java.util.Map;
    31 import java.util.Objects;
    32 import java.util.Set;
    33 import java.util.SortedMap;
    34 import java.util.SortedSet;
    35 import java.util.TreeMap;
    36 import java.util.TreeSet;
    38 import com.sun.tools.classfile.Dependency.Location;
    39 import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
    41 /**
    42  * Dependency Analyzer.
    43  */
    44 public class Analyzer {
    45     /**
    46      * Type of the dependency analysis.  Appropriate level of data
    47      * will be stored.
    48      */
    49     public enum Type {
    50         SUMMARY,
    51         PACKAGE,
    52         CLASS,
    53         VERBOSE
    54     };
    56     /**
    57      * Filter to be applied when analyzing the dependencies from the given archives.
    58      * Only the accepted dependencies are recorded.
    59      */
    60     interface Filter {
    61         boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
    62     }
    64     private final Type type;
    65     private final Filter filter;
    66     private final Map<Archive, ArchiveDeps> results = new HashMap<>();
    67     private final Map<Location, Archive> map = new HashMap<>();
    68     private final Archive NOT_FOUND
    69         = new Archive(JdepsTask.getMessage("artifact.not.found"));
    71     /**
    72      * Constructs an Analyzer instance.
    73      *
    74      * @param type Type of the dependency analysis
    75      * @param filter
    76      */
    77     public Analyzer(Type type, Filter filter) {
    78         this.type = type;
    79         this.filter = filter;
    80     }
    82     /**
    83      * Performs the dependency analysis on the given archives.
    84      */
    85     public void run(List<Archive> archives) {
    86         // build a map from Location to Archive
    87         buildLocationArchiveMap(archives);
    89         // traverse and analyze all dependencies
    90         for (Archive archive : archives) {
    91             ArchiveDeps deps = new ArchiveDeps(archive, type);
    92             archive.visitDependences(deps);
    93             results.put(archive, deps);
    94         }
    95     }
    97     private void buildLocationArchiveMap(List<Archive> archives) {
    98         // build a map from Location to Archive
    99         for (Archive archive: archives) {
   100             for (Location l: archive.getClasses()) {
   101                 if (!map.containsKey(l)) {
   102                     map.put(l, archive);
   103                 } else {
   104                     // duplicated class warning?
   105                 }
   106             }
   107         }
   108     }
   110     public boolean hasDependences(Archive archive) {
   111         if (results.containsKey(archive)) {
   112             return results.get(archive).dependencies().size() > 0;
   113         }
   114         return false;
   115     }
   117     public interface Visitor {
   118         /**
   119          * Visits a recorded dependency from origin to target which can be
   120          * a fully-qualified classname, a package name, a module or
   121          * archive name depending on the Analyzer's type.
   122          */
   123         public void visitDependence(String origin, Archive originArchive,
   124                                     String target, Archive targetArchive);
   125     }
   127     /**
   128      * Visit the dependencies of the given source.
   129      * If the requested level is SUMMARY, it will visit the required archives list.
   130      */
   131     public void visitDependences(Archive source, Visitor v, Type level) {
   132         if (level == Type.SUMMARY) {
   133             final ArchiveDeps result = results.get(source);
   134             SortedMap<String, Archive> sorted = new TreeMap<>();
   135             for (Archive a : result.requires()) {
   136                 sorted.put(a.getName(), a);
   137             }
   138             for (Archive archive : sorted.values()) {
   139                 Profile profile = result.getTargetProfile(archive);
   140                 v.visitDependence(source.getName(), source,
   141                                   profile != null ? profile.profileName() : archive.getName(), archive);
   142             }
   143         } else {
   144             ArchiveDeps result = results.get(source);
   145             if (level != type) {
   146                 // requesting different level of analysis
   147                 result = new ArchiveDeps(source, level);
   148                 source.visitDependences(result);
   149             }
   150             SortedSet<Dep> sorted = new TreeSet<>(result.dependencies());
   151             for (Dep d : sorted) {
   152                 v.visitDependence(d.origin(), d.originArchive(), d.target(), d.targetArchive());
   153             }
   154         }
   155     }
   157     public void visitDependences(Archive source, Visitor v) {
   158         visitDependences(source, v, type);
   159     }
   161     /**
   162      * ArchiveDeps contains the dependencies for an Archive that can have one or
   163      * more classes.
   164      */
   165     class ArchiveDeps implements Archive.Visitor {
   166         protected final Archive archive;
   167         protected final Set<Archive> requires;
   168         protected final Set<Dep> deps;
   169         protected final Type level;
   170         private Profile profile;
   171         ArchiveDeps(Archive archive, Type level) {
   172             this.archive = archive;
   173             this.deps = new HashSet<>();
   174             this.requires = new HashSet<>();
   175             this.level = level;
   176         }
   178         Set<Dep> dependencies() {
   179             return deps;
   180         }
   182         Set<Archive> requires() {
   183             return requires;
   184         }
   186         Profile getTargetProfile(Archive target) {
   187             return JDKArchive.isProfileArchive(target) ? profile : null;
   188         }
   190         Archive findArchive(Location t) {
   191             Archive target = archive.getClasses().contains(t) ? archive : map.get(t);
   192             if (target == null) {
   193                 map.put(t, target = NOT_FOUND);
   194             }
   195             return target;
   196         }
   198         // return classname or package name depedning on the level
   199         private String getLocationName(Location o) {
   200             if (level == Type.CLASS || level == Type.VERBOSE) {
   201                 return o.getClassName();
   202             } else {
   203                 String pkg = o.getPackageName();
   204                 return pkg.isEmpty() ? "<unnamed>" : pkg;
   205             }
   206         }
   208         @Override
   209         public void visit(Location o, Location t) {
   210             Archive targetArchive = findArchive(t);
   211             if (filter.accepts(o, archive, t, targetArchive)) {
   212                 addDep(o, t);
   213                 if (!requires.contains(targetArchive)) {
   214                     requires.add(targetArchive);
   215                 }
   216             }
   217             if (targetArchive instanceof JDKArchive) {
   218                 Profile p = Profile.getProfile(t.getPackageName());
   219                 if (profile == null || (p != null && p.compareTo(profile) > 0)) {
   220                     profile = p;
   221                 }
   222             }
   223         }
   225         private Dep curDep;
   226         protected Dep addDep(Location o, Location t) {
   227             String origin = getLocationName(o);
   228             String target = getLocationName(t);
   229             Archive targetArchive = findArchive(t);
   230             if (curDep != null &&
   231                     curDep.origin().equals(origin) &&
   232                     curDep.originArchive() == archive &&
   233                     curDep.target().equals(target) &&
   234                     curDep.targetArchive() == targetArchive) {
   235                 return curDep;
   236             }
   238             Dep e = new Dep(origin, archive, target, targetArchive);
   239             if (deps.contains(e)) {
   240                 for (Dep e1 : deps) {
   241                     if (e.equals(e1)) {
   242                         curDep = e1;
   243                     }
   244                 }
   245             } else {
   246                 deps.add(e);
   247                 curDep = e;
   248             }
   249             return curDep;
   250         }
   251     }
   253     /*
   254      * Class-level or package-level dependency
   255      */
   256     class Dep implements Comparable<Dep> {
   257         final String origin;
   258         final Archive originArchive;
   259         final String target;
   260         final Archive targetArchive;
   262         Dep(String origin, Archive originArchive, String target, Archive targetArchive) {
   263             this.origin = origin;
   264             this.originArchive = originArchive;
   265             this.target = target;
   266             this.targetArchive = targetArchive;
   267         }
   269         String origin() {
   270             return origin;
   271         }
   273         Archive originArchive() {
   274             return originArchive;
   275         }
   277         String target() {
   278             return target;
   279         }
   281         Archive targetArchive() {
   282             return targetArchive;
   283         }
   285         @Override
   286         @SuppressWarnings("unchecked")
   287         public boolean equals(Object o) {
   288             if (o instanceof Dep) {
   289                 Dep d = (Dep) o;
   290                 return this.origin.equals(d.origin) &&
   291                         this.originArchive == d.originArchive &&
   292                         this.target.equals(d.target) &&
   293                         this.targetArchive == d.targetArchive;
   294             }
   295             return false;
   296         }
   298         @Override
   299         public int hashCode() {
   300             int hash = 7;
   301             hash = 67*hash + Objects.hashCode(this.origin)
   302                            + Objects.hashCode(this.originArchive)
   303                            + Objects.hashCode(this.target)
   304                            + Objects.hashCode(this.targetArchive);
   305             return hash;
   306         }
   308         @Override
   309         public int compareTo(Dep o) {
   310             if (this.origin.equals(o.origin)) {
   311                 if (this.target.equals(o.target)) {
   312                     if (this.originArchive == o.originArchive &&
   313                             this.targetArchive == o.targetArchive) {
   314                         return 0;
   315                     } else if (this.originArchive == o.originArchive) {
   316                         return this.targetArchive.getPathName().compareTo(o.targetArchive.getPathName());
   317                     } else {
   318                         return this.originArchive.getPathName().compareTo(o.originArchive.getPathName());
   319                     }
   320                 } else {
   321                     return this.target.compareTo(o.target);
   322                 }
   323             }
   324             return this.origin.compareTo(o.origin);
   325         }
   326     }
   327 }

mercurial