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

Mon, 16 Oct 2017 16:07:48 +0800

author
aoqi
date
Mon, 16 Oct 2017 16:07:48 +0800
changeset 2893
ca5783d9a597
parent 2802
6b43535fb9f8
parent 2702
9ca8d8713094
permissions
-rw-r--r--

merge

     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 Set<String> dependences(Archive source) {
   118         ArchiveDeps result = results.get(source);
   119         return result.targetDependences();
   120     }
   122     public interface Visitor {
   123         /**
   124          * Visits a recorded dependency from origin to target which can be
   125          * a fully-qualified classname, a package name, a module or
   126          * archive name depending on the Analyzer's type.
   127          */
   128         public void visitDependence(String origin, Archive originArchive,
   129                                     String target, Archive targetArchive);
   130     }
   132     /**
   133      * Visit the dependencies of the given source.
   134      * If the requested level is SUMMARY, it will visit the required archives list.
   135      */
   136     public void visitDependences(Archive source, Visitor v, Type level) {
   137         if (level == Type.SUMMARY) {
   138             final ArchiveDeps result = results.get(source);
   139             SortedMap<String, Archive> sorted = new TreeMap<>();
   140             for (Archive a : result.requires()) {
   141                 sorted.put(a.getName(), a);
   142             }
   143             for (Archive archive : sorted.values()) {
   144                 Profile profile = result.getTargetProfile(archive);
   145                 v.visitDependence(source.getName(), source,
   146                                   profile != null ? profile.profileName() : archive.getName(), archive);
   147             }
   148         } else {
   149             ArchiveDeps result = results.get(source);
   150             if (level != type) {
   151                 // requesting different level of analysis
   152                 result = new ArchiveDeps(source, level);
   153                 source.visitDependences(result);
   154             }
   155             SortedSet<Dep> sorted = new TreeSet<>(result.dependencies());
   156             for (Dep d : sorted) {
   157                 v.visitDependence(d.origin(), d.originArchive(), d.target(), d.targetArchive());
   158             }
   159         }
   160     }
   162     public void visitDependences(Archive source, Visitor v) {
   163         visitDependences(source, v, type);
   164     }
   166     /**
   167      * ArchiveDeps contains the dependencies for an Archive that can have one or
   168      * more classes.
   169      */
   170     class ArchiveDeps implements Archive.Visitor {
   171         protected final Archive archive;
   172         protected final Set<Archive> requires;
   173         protected final Set<Dep> deps;
   174         protected final Type level;
   175         private Profile profile;
   176         ArchiveDeps(Archive archive, Type level) {
   177             this.archive = archive;
   178             this.deps = new HashSet<>();
   179             this.requires = new HashSet<>();
   180             this.level = level;
   181         }
   183         Set<Dep> dependencies() {
   184             return deps;
   185         }
   187         Set<String> targetDependences() {
   188             Set<String> targets = new HashSet<>();
   189             for (Dep d : deps) {
   190                 targets.add(d.target());
   191             }
   192             return targets;
   193         }
   195         Set<Archive> requires() {
   196             return requires;
   197         }
   199         Profile getTargetProfile(Archive target) {
   200             return JDKArchive.isProfileArchive(target) ? profile : null;
   201         }
   203         Archive findArchive(Location t) {
   204             Archive target = archive.getClasses().contains(t) ? archive : map.get(t);
   205             if (target == null) {
   206                 map.put(t, target = NOT_FOUND);
   207             }
   208             return target;
   209         }
   211         // return classname or package name depedning on the level
   212         private String getLocationName(Location o) {
   213             if (level == Type.CLASS || level == Type.VERBOSE) {
   214                 return o.getClassName();
   215             } else {
   216                 String pkg = o.getPackageName();
   217                 return pkg.isEmpty() ? "<unnamed>" : pkg;
   218             }
   219         }
   221         @Override
   222         public void visit(Location o, Location t) {
   223             Archive targetArchive = findArchive(t);
   224             if (filter.accepts(o, archive, t, targetArchive)) {
   225                 addDep(o, t);
   226                 if (archive != targetArchive && !requires.contains(targetArchive)) {
   227                     requires.add(targetArchive);
   228                 }
   229             }
   230             if (targetArchive instanceof JDKArchive) {
   231                 Profile p = Profile.getProfile(t.getPackageName());
   232                 if (profile == null || (p != null && p.compareTo(profile) > 0)) {
   233                     profile = p;
   234                 }
   235             }
   236         }
   238         private Dep curDep;
   239         protected Dep addDep(Location o, Location t) {
   240             String origin = getLocationName(o);
   241             String target = getLocationName(t);
   242             Archive targetArchive = findArchive(t);
   243             if (curDep != null &&
   244                     curDep.origin().equals(origin) &&
   245                     curDep.originArchive() == archive &&
   246                     curDep.target().equals(target) &&
   247                     curDep.targetArchive() == targetArchive) {
   248                 return curDep;
   249             }
   251             Dep e = new Dep(origin, archive, target, targetArchive);
   252             if (deps.contains(e)) {
   253                 for (Dep e1 : deps) {
   254                     if (e.equals(e1)) {
   255                         curDep = e1;
   256                     }
   257                 }
   258             } else {
   259                 deps.add(e);
   260                 curDep = e;
   261             }
   262             return curDep;
   263         }
   264     }
   266     /*
   267      * Class-level or package-level dependency
   268      */
   269     class Dep implements Comparable<Dep> {
   270         final String origin;
   271         final Archive originArchive;
   272         final String target;
   273         final Archive targetArchive;
   275         Dep(String origin, Archive originArchive, String target, Archive targetArchive) {
   276             this.origin = origin;
   277             this.originArchive = originArchive;
   278             this.target = target;
   279             this.targetArchive = targetArchive;
   280         }
   282         String origin() {
   283             return origin;
   284         }
   286         Archive originArchive() {
   287             return originArchive;
   288         }
   290         String target() {
   291             return target;
   292         }
   294         Archive targetArchive() {
   295             return targetArchive;
   296         }
   298         @Override
   299         @SuppressWarnings("unchecked")
   300         public boolean equals(Object o) {
   301             if (o instanceof Dep) {
   302                 Dep d = (Dep) o;
   303                 return this.origin.equals(d.origin) &&
   304                         this.originArchive == d.originArchive &&
   305                         this.target.equals(d.target) &&
   306                         this.targetArchive == d.targetArchive;
   307             }
   308             return false;
   309         }
   311         @Override
   312         public int hashCode() {
   313             int hash = 7;
   314             hash = 67*hash + Objects.hashCode(this.origin)
   315                            + Objects.hashCode(this.originArchive)
   316                            + Objects.hashCode(this.target)
   317                            + Objects.hashCode(this.targetArchive);
   318             return hash;
   319         }
   321         @Override
   322         public int compareTo(Dep o) {
   323             if (this.origin.equals(o.origin)) {
   324                 if (this.target.equals(o.target)) {
   325                     if (this.originArchive == o.originArchive &&
   326                             this.targetArchive == o.targetArchive) {
   327                         return 0;
   328                     } else if (this.originArchive == o.originArchive) {
   329                         return this.targetArchive.getPathName().compareTo(o.targetArchive.getPathName());
   330                     } else {
   331                         return this.originArchive.getPathName().compareTo(o.originArchive.getPathName());
   332                     }
   333                 } else {
   334                     return this.target.compareTo(o.target);
   335                 }
   336             }
   337             return this.origin.compareTo(o.origin);
   338         }
   339     }
   340 }

mercurial