mchung@1577: /* mchung@1577: * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. mchung@1577: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mchung@1577: * mchung@1577: * This code is free software; you can redistribute it and/or modify it mchung@1577: * under the terms of the GNU General Public License version 2 only, as mchung@1577: * published by the Free Software Foundation. Oracle designates this mchung@1577: * particular file as subject to the "Classpath" exception as provided mchung@1577: * by Oracle in the LICENSE file that accompanied this code. mchung@1577: * mchung@1577: * This code is distributed in the hope that it will be useful, but WITHOUT mchung@1577: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mchung@1577: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mchung@1577: * version 2 for more details (a copy is included in the LICENSE file that mchung@1577: * accompanied this code). mchung@1577: * mchung@1577: * You should have received a copy of the GNU General Public License version mchung@1577: * 2 along with this work; if not, write to the Free Software Foundation, mchung@1577: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mchung@1577: * mchung@1577: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA mchung@1577: * or visit www.oracle.com if you need additional information or have any mchung@1577: * questions. mchung@1577: */ mchung@1577: package com.sun.tools.jdeps; mchung@1577: mchung@1577: import com.sun.tools.classfile.Dependency.Location; mchung@2139: import com.sun.tools.jdeps.PlatformClassPath.JDKArchive; mchung@1577: import java.util.HashMap; mchung@1577: import java.util.List; mchung@1577: import java.util.Map; mchung@1577: import java.util.Set; mchung@1577: import java.util.SortedMap; mchung@1577: import java.util.SortedSet; mchung@1577: import java.util.TreeMap; mchung@1577: import java.util.TreeSet; mchung@1577: mchung@1577: /** mchung@1577: * Dependency Analyzer. mchung@1577: */ mchung@1577: public class Analyzer { mchung@1577: /** mchung@1577: * Type of the dependency analysis. Appropriate level of data mchung@1577: * will be stored. mchung@1577: */ mchung@1577: public enum Type { mchung@1577: SUMMARY, mchung@1577: PACKAGE, mchung@1577: CLASS, mchung@1577: VERBOSE mchung@1577: }; mchung@1577: mchung@1577: private final Type type; mchung@2139: private final Map results = new HashMap<>(); mchung@2139: private final Map map = new HashMap<>(); mchung@1577: private final Archive NOT_FOUND mchung@1577: = new Archive(JdepsTask.getMessage("artifact.not.found")); mchung@1577: mchung@1577: /** mchung@1577: * Constructs an Analyzer instance. mchung@1577: * mchung@1577: * @param type Type of the dependency analysis mchung@1577: */ mchung@1577: public Analyzer(Type type) { mchung@1577: this.type = type; mchung@1577: } mchung@1577: mchung@1577: /** mchung@1577: * Performs the dependency analysis on the given archives. mchung@1577: */ mchung@1577: public void run(List archives) { mchung@1577: for (Archive archive : archives) { mchung@1577: ArchiveDeps deps; mchung@1577: if (type == Type.CLASS || type == Type.VERBOSE) { mchung@1577: deps = new ClassVisitor(archive); mchung@1577: } else { mchung@1577: deps = new PackageVisitor(archive); mchung@1577: } mchung@1577: archive.visit(deps); mchung@2139: results.put(archive, deps); mchung@1577: } mchung@1577: mchung@1577: // set the required dependencies mchung@2139: for (ArchiveDeps result: results.values()) { mchung@1577: for (Set set : result.deps.values()) { mchung@1577: for (String target : set) { mchung@1577: Archive source = getArchive(target); mchung@1577: if (result.archive != source) { mchung@2139: String profile = ""; mchung@2139: if (source instanceof JDKArchive) { mchung@2139: profile = result.profile != null ? result.profile.toString() : ""; mchung@2139: if (result.getTargetProfile(target) == null) { mchung@2139: profile += ", JDK internal API"; mchung@2139: // override the value if it accesses any JDK internal mchung@2139: result.requireArchives.put(source, profile); mchung@2139: continue; mchung@2139: } mchung@1577: } mchung@2139: if (!result.requireArchives.containsKey(source)) { mchung@2139: result.requireArchives.put(source, profile); mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: mchung@2139: public boolean hasDependences(Archive archive) { mchung@2139: if (results.containsKey(archive)) { mchung@2139: return results.get(archive).deps.size() > 0; mchung@2139: } mchung@2139: return false; mchung@2139: } mchung@2139: mchung@1577: public interface Visitor { mchung@1577: /** mchung@2139: * Visits the source archive to its destination archive of mchung@2139: * a recorded dependency. mchung@2139: */ mchung@2139: void visitArchiveDependence(Archive origin, Archive target, String profile); mchung@2139: /** mchung@1577: * Visits a recorded dependency from origin to target which can be mchung@1577: * a fully-qualified classname, a package name, a profile or mchung@1577: * archive name depending on the Analyzer's type. mchung@1577: */ mchung@2139: void visitDependence(String origin, Archive source, String target, Archive archive, String profile); mchung@1577: } mchung@1577: mchung@2139: public void visitArchiveDependences(Archive source, Visitor v) { mchung@2139: ArchiveDeps r = results.get(source); mchung@2139: for (Map.Entry e : r.requireArchives.entrySet()) { mchung@2139: v.visitArchiveDependence(r.archive, e.getKey(), e.getValue()); mchung@1577: } mchung@1577: } mchung@1577: mchung@2139: public void visitDependences(Archive source, Visitor v) { mchung@2139: ArchiveDeps r = results.get(source); mchung@2139: for (String origin : r.deps.keySet()) { mchung@2139: for (String target : r.deps.get(origin)) { mchung@2139: Archive archive = getArchive(target); mchung@2139: assert source == getArchive(origin); mchung@2139: Profile profile = r.getTargetProfile(target); mchung@2139: mchung@2139: // filter intra-dependency unless in verbose mode mchung@2139: if (type == Type.VERBOSE || archive != source) { mchung@2139: v.visitDependence(origin, source, target, archive, mchung@2139: profile != null ? profile.toString() : ""); mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: mchung@1577: public Archive getArchive(String name) { mchung@1577: return map.containsKey(name) ? map.get(name) : NOT_FOUND; mchung@1577: } mchung@1577: mchung@1577: private abstract class ArchiveDeps implements Archive.Visitor { mchung@1577: final Archive archive; mchung@2139: final Map requireArchives; mchung@1577: final SortedMap> deps; mchung@2139: Profile profile = null; mchung@1577: ArchiveDeps(Archive archive) { mchung@1577: this.archive = archive; mchung@2139: this.requireArchives = new HashMap<>(); mchung@2139: this.deps = new TreeMap<>(); mchung@1577: } mchung@1577: mchung@1577: void add(String loc) { mchung@1577: Archive a = map.get(loc); mchung@1577: if (a == null) { mchung@1577: map.put(loc, archive); mchung@1577: } else if (a != archive) { mchung@1577: // duplicated class warning? mchung@1577: } mchung@1577: } mchung@1577: mchung@1577: void add(String origin, String target) { mchung@1577: SortedSet set = deps.get(origin); mchung@1577: if (set == null) { mchung@2139: deps.put(origin, set = new TreeSet<>()); mchung@1577: } mchung@1577: if (!set.contains(target)) { mchung@1577: set.add(target); mchung@2139: // find the corresponding profile mchung@2139: Profile p = getTargetProfile(target); mchung@2139: if (profile == null || (p != null && profile.profile < p.profile)) { mchung@2139: profile = p; mchung@2139: } mchung@1577: } mchung@1577: } mchung@1577: public abstract void visit(Location o, Location t); mchung@2139: public abstract Profile getTargetProfile(String target); mchung@1577: } mchung@1577: mchung@1577: private class ClassVisitor extends ArchiveDeps { mchung@1577: ClassVisitor(Archive archive) { mchung@1577: super(archive); mchung@1577: } mchung@1577: public void visit(Location l) { mchung@1577: add(l.getClassName()); mchung@1577: } mchung@1577: public void visit(Location o, Location t) { mchung@1577: add(o.getClassName(), t.getClassName()); mchung@1577: } mchung@2139: public Profile getTargetProfile(String target) { mchung@1638: int i = target.lastIndexOf('.'); mchung@2139: return (i > 0) ? Profile.getProfile(target.substring(0, i)) : null; mchung@1638: } mchung@1577: } mchung@1577: mchung@1577: private class PackageVisitor extends ArchiveDeps { mchung@1577: PackageVisitor(Archive archive) { mchung@1577: super(archive); mchung@1577: } mchung@1577: public void visit(Location o, Location t) { mchung@1577: add(packageOf(o), packageOf(t)); mchung@1577: } mchung@1577: public void visit(Location l) { mchung@1577: add(packageOf(l)); mchung@1577: } mchung@1577: private String packageOf(Location loc) { mchung@1577: String pkg = loc.getPackageName(); mchung@1577: return pkg.isEmpty() ? "" : pkg; mchung@1577: } mchung@2139: public Profile getTargetProfile(String target) { mchung@2139: return Profile.getProfile(target); mchung@1638: } mchung@1577: } mchung@1577: }