< prev index next >

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

Print this page




  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 
  26 package com.sun.tools.jdeps;
  27 
  28 import com.sun.tools.classfile.Dependency.Location;
  29 
  30 import java.io.BufferedReader;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.InputStreamReader;
  34 import java.io.UncheckedIOException;

  35 import java.util.Collections;
  36 import java.util.Comparator;
  37 import java.util.HashMap;
  38 import java.util.HashSet;
  39 import java.util.Map;
  40 import java.util.Objects;
  41 import java.util.Set;

  42 import java.util.stream.Collectors;
  43 import java.util.stream.Stream;
  44 
  45 /**
  46  * Dependency Analyzer.
  47  */
  48 public class Analyzer {
  49     /**
  50      * Type of the dependency analysis.  Appropriate level of data
  51      * will be stored.
  52      */
  53     public enum Type {
  54         SUMMARY,
  55         MODULE,  // equivalent to summary in addition, print module descriptor
  56         PACKAGE,
  57         CLASS,
  58         VERBOSE
  59     }
  60 
  61     /**
  62      * Filter to be applied when analyzing the dependencies from the given archives.
  63      * Only the accepted dependencies are recorded.
  64      */
  65     interface Filter {
  66         boolean accepts(Location origin, Archive originArchive,
  67                         Location target, Archive targetArchive);
  68     }
  69 
  70     protected final JdepsConfiguration configuration;
  71     protected final Type type;
  72     protected final Filter filter;
  73     protected final Map<Archive, Dependences> results = new HashMap<>();
  74     protected final Map<Location, Archive> locationToArchive = new HashMap<>();
  75     static final Archive NOT_FOUND
  76         = new Archive(JdepsTask.getMessage("artifact.not.found"));

  77 
  78     /**
  79      * Constructs an Analyzer instance.
  80      *
  81      * @param type Type of the dependency analysis
  82      * @param filter
  83      */
  84     Analyzer(JdepsConfiguration config, Type type, Filter filter) {
  85         this.configuration = config;
  86         this.type = type;
  87         this.filter = filter;
  88     }
  89 
  90     /**
  91      * Performs the dependency analysis on the given archives.
  92      */
  93     boolean run(Iterable<? extends Archive> archives,
  94                 Map<Location, Archive> locationMap)
  95     {
  96         this.locationToArchive.putAll(locationMap);


 144             return Stream.empty();
 145         }
 146         return results.get(source).requires()
 147                       .stream();
 148     }
 149 
 150     interface Visitor {
 151         /**
 152          * Visits a recorded dependency from origin to target which can be
 153          * a fully-qualified classname, a package name, a module or
 154          * archive name depending on the Analyzer's type.
 155          */
 156         public void visitDependence(String origin, Archive originArchive,
 157                                     String target, Archive targetArchive);
 158     }
 159 
 160     /**
 161      * Visit the dependencies of the given source.
 162      * If the requested level is SUMMARY, it will visit the required archives list.
 163      */
 164     void visitDependences(Archive source, Visitor v, Type level) {
 165         if (level == Type.SUMMARY) {
 166             final Dependences result = results.get(source);
 167             final Set<Archive> reqs = result.requires();
 168             Stream<Archive> stream = reqs.stream();
 169             if (reqs.isEmpty()) {
 170                 if (hasDependences(source)) {
 171                     // If reqs.isEmpty() and we have dependences, then it means
 172                     // that the dependences are from 'source' onto itself.
 173                     stream = Stream.of(source);
 174                 }
 175             }
 176             stream.sorted(Comparator.comparing(Archive::getName))
 177                   .forEach(archive -> {
 178                       Profile profile = result.getTargetProfile(archive);
 179                       v.visitDependence(source.getName(), source,
 180                                         profile != null ? profile.profileName()
 181                                                         : archive.getName(), archive);
 182                   });
 183         } else {
 184             Dependences result = results.get(source);
 185             if (level != type) {
 186                 // requesting different level of analysis
 187                 result = new Dependences(source, level);
 188                 source.visitDependences(result);
 189             }
 190             result.dependencies().stream()
 191                   .sorted(Comparator.comparing(Dep::origin)
 192                                     .thenComparing(Dep::target))
 193                   .forEach(d -> v.visitDependence(d.origin(), d.originArchive(),
 194                                                   d.target(), d.targetArchive()));
 195         }
 196     }
 197 
 198     void visitDependences(Archive source, Visitor v) {
 199         visitDependences(source, v, type);




 200     }
 201 
 202     /**
 203      * Dependences contains the dependencies for an Archive that can have one or
 204      * more classes.
 205      */
 206     class Dependences implements Archive.Visitor {
 207         protected final Archive archive;
 208         protected final Set<Archive> requires;
 209         protected final Set<Dep> deps;
 210         protected final Type level;

 211         private Profile profile;
 212         Dependences(Archive archive, Type level) {



 213             this.archive = archive;
 214             this.deps = new HashSet<>();
 215             this.requires = new HashSet<>();
 216             this.level = level;

 217         }
 218 
 219         Set<Dep> dependencies() {
 220             return deps;
 221         }
 222 
 223         Set<Archive> requires() {
 224             return requires;
 225         }
 226 
 227         Profile getTargetProfile(Archive target) {
 228             if (target.getModule().isJDK()) {
 229                 return Profile.getProfile((Module) target);
 230             } else {
 231                 return null;
 232             }
 233         }
 234 
 235         /*
 236          * Returns the archive that contains the given location.


 249                     .orElseGet(() -> REMOVED_JDK_INTERNALS.contains(t)
 250                                         ? REMOVED_JDK_INTERNALS
 251                                         : NOT_FOUND);
 252             }
 253             return locationToArchive.computeIfAbsent(t, _k -> target);
 254         }
 255 
 256         // return classname or package name depending on the level
 257         private String getLocationName(Location o) {
 258             if (level == Type.CLASS || level == Type.VERBOSE) {
 259                 return VersionHelper.get(o.getClassName());
 260             } else {
 261                 String pkg = o.getPackageName();
 262                 return pkg.isEmpty() ? "<unnamed>" : pkg;
 263             }
 264         }
 265 
 266         @Override
 267         public void visit(Location o, Location t) {
 268             Archive targetArchive = findArchive(t);
 269             if (filter.accepts(o, archive, t, targetArchive)) {
 270                 addDep(o, t);
 271                 if (archive != targetArchive && !requires.contains(targetArchive)) {
 272                     requires.add(targetArchive);
 273                 }
 274             }
 275             if (targetArchive.getModule().isNamed()) {
 276                 Profile p = Profile.getProfile(t.getPackageName());
 277                 if (profile == null || (p != null && p.compareTo(profile) > 0)) {
 278                     profile = p;
 279                 }
 280             }
 281         }
 282 
 283         private Dep curDep;
 284         protected Dep addDep(Location o, Location t) {
 285             String origin = getLocationName(o);
 286             String target = getLocationName(t);
 287             Archive targetArchive = findArchive(t);
 288             if (curDep != null &&
 289                     curDep.origin().equals(origin) &&


 351                         this.targetArchive == d.targetArchive;
 352             }
 353             return false;
 354         }
 355 
 356         @Override
 357         public int hashCode() {
 358             return Objects.hash(this.origin,
 359                                 this.originArchive,
 360                                 this.target,
 361                                 this.targetArchive);
 362         }
 363 
 364         public String toString() {
 365             return String.format("%s (%s) -> %s (%s)%n",
 366                     origin, originArchive.getName(),
 367                     target, targetArchive.getName());
 368         }
 369     }
 370 







 371     static final Jdk8Internals REMOVED_JDK_INTERNALS = new Jdk8Internals();
 372 
 373     static class Jdk8Internals extends Module {
 374         private final String JDK8_INTERNALS = "/com/sun/tools/jdeps/resources/jdk8_internals.txt";

 375         private final Set<String> jdk8Internals;
 376         private Jdk8Internals() {
 377             super("JDK removed internal API");
 378             try (InputStream in = JdepsTask.class.getResourceAsStream(JDK8_INTERNALS);
 379                  BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
 380                 this.jdk8Internals = reader.lines()
 381                                           .filter(ln -> !ln.startsWith("#"))
 382                                           .collect(Collectors.toSet());
 383             } catch (IOException e) {
 384                 throw new UncheckedIOException(e);
 385             }
 386         }
 387 
 388         public boolean contains(Location location) {
 389             String cn = location.getClassName();
 390             int i = cn.lastIndexOf('.');
 391             String pn = i > 0 ? cn.substring(0, i) : "";
 392 
 393             return jdk8Internals.contains(pn);
 394         }
 395 
 396         @Override
 397         public String name() {
 398             return getName();
 399         }
 400 
 401         @Override
 402         public boolean isJDK() {
 403             return true;
 404         }
 405 
 406         @Override
 407         public boolean isExported(String pn) {
 408             return false;
 409         }
 410     }
 411 }


  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 
  26 package com.sun.tools.jdeps;
  27 
  28 import com.sun.tools.classfile.Dependency.Location;
  29 
  30 import java.io.BufferedReader;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.InputStreamReader;
  34 import java.io.UncheckedIOException;
  35 import java.lang.module.ModuleDescriptor;
  36 import java.util.Collections;
  37 import java.util.Comparator;
  38 import java.util.HashMap;
  39 import java.util.HashSet;
  40 import java.util.Map;
  41 import java.util.Objects;
  42 import java.util.Set;
  43 import java.util.function.Predicate;
  44 import java.util.stream.Collectors;
  45 import java.util.stream.Stream;
  46 
  47 /**
  48  * Dependency Analyzer.
  49  */
  50 public class Analyzer {
  51     /**
  52      * Type of the dependency analysis.  Appropriate level of data
  53      * will be stored.
  54      */
  55     public enum Type {
  56         SUMMARY,
  57         MODULE,  // equivalent to summary in addition, print module descriptor
  58         PACKAGE,
  59         CLASS,
  60         VERBOSE
  61     }
  62 
  63     /**
  64      * Filter to be applied when analyzing the dependencies from the given archives.
  65      * Only the accepted dependencies are recorded.
  66      */
  67     interface Filter {
  68         boolean accepts(Location origin, Archive originArchive,
  69                         Location target, Archive targetArchive);
  70     }
  71 
  72     protected final JdepsConfiguration configuration;
  73     protected final Type type;
  74     protected final Filter filter;
  75     protected final Map<Archive, Dependences> results = new HashMap<>();
  76     protected final Map<Location, Archive> locationToArchive = new HashMap<>();
  77     static final Archive NOT_FOUND
  78         = new Archive(JdepsTask.getMessage("artifact.not.found"));
  79     static final Predicate<Archive> ANY = a -> true;
  80 
  81     /**
  82      * Constructs an Analyzer instance.
  83      *
  84      * @param type Type of the dependency analysis
  85      * @param filter
  86      */
  87     Analyzer(JdepsConfiguration config, Type type, Filter filter) {
  88         this.configuration = config;
  89         this.type = type;
  90         this.filter = filter;
  91     }
  92 
  93     /**
  94      * Performs the dependency analysis on the given archives.
  95      */
  96     boolean run(Iterable<? extends Archive> archives,
  97                 Map<Location, Archive> locationMap)
  98     {
  99         this.locationToArchive.putAll(locationMap);


 147             return Stream.empty();
 148         }
 149         return results.get(source).requires()
 150                       .stream();
 151     }
 152 
 153     interface Visitor {
 154         /**
 155          * Visits a recorded dependency from origin to target which can be
 156          * a fully-qualified classname, a package name, a module or
 157          * archive name depending on the Analyzer's type.
 158          */
 159         public void visitDependence(String origin, Archive originArchive,
 160                                     String target, Archive targetArchive);
 161     }
 162 
 163     /**
 164      * Visit the dependencies of the given source.
 165      * If the requested level is SUMMARY, it will visit the required archives list.
 166      */
 167     void visitDependences(Archive source, Visitor v, Type level, Predicate<Archive> targetFilter) {
 168         if (level == Type.SUMMARY) {
 169             final Dependences result = results.get(source);
 170             final Set<Archive> reqs = result.requires();
 171             Stream<Archive> stream = reqs.stream();
 172             if (reqs.isEmpty()) {
 173                 if (hasDependences(source)) {
 174                     // If reqs.isEmpty() and we have dependences, then it means
 175                     // that the dependences are from 'source' onto itself.
 176                     stream = Stream.of(source);
 177                 }
 178             }
 179             stream.sorted(Comparator.comparing(Archive::getName))
 180                   .forEach(archive -> {
 181                       Profile profile = result.getTargetProfile(archive);
 182                       v.visitDependence(source.getName(), source,
 183                                         profile != null ? profile.profileName()
 184                                                         : archive.getName(), archive);
 185                   });
 186         } else {
 187             Dependences result = results.get(source);
 188             if (level != type) {
 189                 // requesting different level of analysis
 190                 result = new Dependences(source, level, targetFilter);
 191                 source.visitDependences(result);
 192             }
 193             result.dependencies().stream()
 194                   .sorted(Comparator.comparing(Dep::origin)
 195                                     .thenComparing(Dep::target))
 196                   .forEach(d -> v.visitDependence(d.origin(), d.originArchive(),
 197                                                   d.target(), d.targetArchive()));
 198         }
 199     }
 200 
 201     void visitDependences(Archive source, Visitor v) {
 202         visitDependences(source, v, type, ANY);
 203     }
 204 
 205     void visitDependences(Archive source, Visitor v, Type level) {
 206         visitDependences(source, v, level, ANY);
 207     }
 208 
 209     /**
 210      * Dependences contains the dependencies for an Archive that can have one or
 211      * more classes.
 212      */
 213     class Dependences implements Archive.Visitor {
 214         protected final Archive archive;
 215         protected final Set<Archive> requires;
 216         protected final Set<Dep> deps;
 217         protected final Type level;
 218         protected final Predicate<Archive> targetFilter;
 219         private Profile profile;
 220         Dependences(Archive archive, Type level) {
 221             this(archive, level, ANY);
 222         }
 223         Dependences(Archive archive, Type level, Predicate<Archive> targetFilter) {
 224             this.archive = archive;
 225             this.deps = new HashSet<>();
 226             this.requires = new HashSet<>();
 227             this.level = level;
 228             this.targetFilter = targetFilter;
 229         }
 230 
 231         Set<Dep> dependencies() {
 232             return deps;
 233         }
 234 
 235         Set<Archive> requires() {
 236             return requires;
 237         }
 238 
 239         Profile getTargetProfile(Archive target) {
 240             if (target.getModule().isJDK()) {
 241                 return Profile.getProfile((Module) target);
 242             } else {
 243                 return null;
 244             }
 245         }
 246 
 247         /*
 248          * Returns the archive that contains the given location.


 261                     .orElseGet(() -> REMOVED_JDK_INTERNALS.contains(t)
 262                                         ? REMOVED_JDK_INTERNALS
 263                                         : NOT_FOUND);
 264             }
 265             return locationToArchive.computeIfAbsent(t, _k -> target);
 266         }
 267 
 268         // return classname or package name depending on the level
 269         private String getLocationName(Location o) {
 270             if (level == Type.CLASS || level == Type.VERBOSE) {
 271                 return VersionHelper.get(o.getClassName());
 272             } else {
 273                 String pkg = o.getPackageName();
 274                 return pkg.isEmpty() ? "<unnamed>" : pkg;
 275             }
 276         }
 277 
 278         @Override
 279         public void visit(Location o, Location t) {
 280             Archive targetArchive = findArchive(t);
 281             if (filter.accepts(o, archive, t, targetArchive) && targetFilter.test(targetArchive)) {
 282                 addDep(o, t);
 283                 if (archive != targetArchive && !requires.contains(targetArchive)) {
 284                     requires.add(targetArchive);
 285                 }
 286             }
 287             if (targetArchive.getModule().isNamed()) {
 288                 Profile p = Profile.getProfile(t.getPackageName());
 289                 if (profile == null || (p != null && p.compareTo(profile) > 0)) {
 290                     profile = p;
 291                 }
 292             }
 293         }
 294 
 295         private Dep curDep;
 296         protected Dep addDep(Location o, Location t) {
 297             String origin = getLocationName(o);
 298             String target = getLocationName(t);
 299             Archive targetArchive = findArchive(t);
 300             if (curDep != null &&
 301                     curDep.origin().equals(origin) &&


 363                         this.targetArchive == d.targetArchive;
 364             }
 365             return false;
 366         }
 367 
 368         @Override
 369         public int hashCode() {
 370             return Objects.hash(this.origin,
 371                                 this.originArchive,
 372                                 this.target,
 373                                 this.targetArchive);
 374         }
 375 
 376         public String toString() {
 377             return String.format("%s (%s) -> %s (%s)%n",
 378                     origin, originArchive.getName(),
 379                     target, targetArchive.getName());
 380         }
 381     }
 382 
 383     /*
 384      * Returns true if the given archive represents not found.
 385      */
 386     static boolean notFound(Archive archive) {
 387         return archive == NOT_FOUND || archive == REMOVED_JDK_INTERNALS;
 388     }
 389 
 390     static final Jdk8Internals REMOVED_JDK_INTERNALS = new Jdk8Internals();
 391 
 392     static class Jdk8Internals extends Module {
 393         private static final String NAME = "JDK removed internal API";
 394         private static final String JDK8_INTERNALS = "/com/sun/tools/jdeps/resources/jdk8_internals.txt";
 395         private final Set<String> jdk8Internals;
 396         private Jdk8Internals() {
 397             super(NAME, ModuleDescriptor.newModule("jdk8internals").build(), true);
 398             try (InputStream in = JdepsTask.class.getResourceAsStream(JDK8_INTERNALS);
 399                  BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
 400                 this.jdk8Internals = reader.lines()
 401                                           .filter(ln -> !ln.startsWith("#"))
 402                                           .collect(Collectors.toSet());
 403             } catch (IOException e) {
 404                 throw new UncheckedIOException(e);
 405             }
 406         }
 407 
 408         public boolean contains(Location location) {
 409             String cn = location.getClassName();
 410             int i = cn.lastIndexOf('.');
 411             String pn = i > 0 ? cn.substring(0, i) : "";
 412 
 413             return jdk8Internals.contains(pn);
 414         }
 415 
 416         @Override





 417         public boolean isJDK() {
 418             return true;
 419         }
 420 
 421         @Override
 422         public boolean isExported(String pn) {
 423             return false;
 424         }
 425     }
 426 }
< prev index next >