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@2172: import java.util.ArrayList; mchung@1577: import java.util.HashMap; mchung@1577: import java.util.List; mchung@1577: import java.util.Map; mchung@2172: import java.util.Objects; 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@2172: 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@2172: // build a map from Location to Archive mchung@2172: for (Archive archive: archives) { mchung@2172: for (Location l: archive.getClasses()) { mchung@2172: if (!map.containsKey(l)) { mchung@2172: map.put(l, archive); mchung@2172: } else { mchung@2172: // duplicated class warning? mchung@2172: } mchung@2172: } mchung@2172: } mchung@2172: // traverse and analyze all dependencies 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@2172: archive.visitDependences(deps); mchung@2139: results.put(archive, deps); 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@2172: void visitArchiveDependence(Archive origin, Archive target, Profile 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@2172: void visitDependence(String origin, Archive source, String target, Archive archive, Profile profile); mchung@1577: } mchung@1577: mchung@2139: public void visitArchiveDependences(Archive source, Visitor v) { mchung@2139: ArchiveDeps r = results.get(source); mchung@2172: for (ArchiveDeps.Dep d: r.requireArchives()) { mchung@2172: v.visitArchiveDependence(r.archive, d.archive, d.profile); mchung@1577: } mchung@1577: } mchung@1577: mchung@2139: public void visitDependences(Archive source, Visitor v) { mchung@2139: ArchiveDeps r = results.get(source); mchung@2172: for (Map.Entry> e: r.deps.entrySet()) { mchung@2172: String origin = e.getKey(); mchung@2172: for (ArchiveDeps.Dep d: e.getValue()) { mchung@2139: // filter intra-dependency unless in verbose mode mchung@2172: if (type == Type.VERBOSE || d.archive != source) { mchung@2172: v.visitDependence(origin, source, d.target, d.archive, d.profile); mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: mchung@2172: /** mchung@2172: * ArchiveDeps contains the dependencies for an Archive that mchung@2172: * can have one or more classes. mchung@2172: */ mchung@1577: private abstract class ArchiveDeps implements Archive.Visitor { mchung@1577: final Archive archive; mchung@2172: final SortedMap> deps; mchung@1577: ArchiveDeps(Archive archive) { mchung@1577: this.archive = archive; mchung@2139: this.deps = new TreeMap<>(); mchung@1577: } mchung@1577: mchung@2172: void add(String origin, String target, Archive targetArchive, String pkgName) { mchung@2172: SortedSet set = deps.get(origin); mchung@1577: if (set == null) { mchung@2139: deps.put(origin, set = new TreeSet<>()); mchung@1577: } mchung@2172: Profile p = targetArchive instanceof JDKArchive mchung@2172: ? Profile.getProfile(pkgName) : null; mchung@2172: set.add(new Dep(target, targetArchive, p)); mchung@2172: } mchung@2172: mchung@2172: /** mchung@2172: * Returns the list of Archive dependences. The returned mchung@2172: * list contains one {@code Dep} instance per one archive mchung@2172: * and with the minimum profile this archive depends on. mchung@2172: */ mchung@2172: List requireArchives() { mchung@2172: Map map = new HashMap<>(); mchung@2172: for (Set set: deps.values()) { mchung@2172: for (Dep d: set) { mchung@2172: if (this.archive != d.archive) { mchung@2172: Profile p = map.get(d.archive); mchung@2172: if (p == null || (d.profile != null && p.profile < d.profile.profile)) { mchung@2172: map.put(d.archive, d.profile); mchung@2172: } mchung@2172: } mchung@2139: } mchung@1577: } mchung@2172: List list = new ArrayList<>(); mchung@2172: for (Map.Entry e: map.entrySet()) { mchung@2172: list.add(new Dep("", e.getKey(), e.getValue())); mchung@2172: } mchung@2172: return list; mchung@2172: } mchung@2172: mchung@2172: /** mchung@2172: * Dep represents a dependence where the target can be mchung@2172: * a classname or packagename and the archive and profile mchung@2172: * the target belongs to. mchung@2172: */ mchung@2172: class Dep implements Comparable { mchung@2172: final String target; mchung@2172: final Archive archive; mchung@2172: final Profile profile; mchung@2172: Dep(String target, Archive archive, Profile p) { mchung@2172: this.target = target; mchung@2172: this.archive = archive; mchung@2172: this.profile = p; mchung@2172: } mchung@2172: mchung@2172: @Override mchung@2172: public boolean equals(Object o) { mchung@2172: if (o instanceof Dep) { mchung@2172: Dep d = (Dep)o; mchung@2172: return this.archive == d.archive && this.target.equals(d.target); mchung@2172: } mchung@2172: return false; mchung@2172: } mchung@2172: mchung@2172: @Override mchung@2172: public int hashCode() { mchung@2172: int hash = 3; mchung@2172: hash = 17 * hash + Objects.hashCode(this.archive); mchung@2172: hash = 17 * hash + Objects.hashCode(this.target); mchung@2172: return hash; mchung@2172: } mchung@2172: mchung@2172: @Override mchung@2172: public int compareTo(Dep o) { mchung@2172: if (this.target.equals(o.target)) { mchung@2172: if (this.archive == o.archive) { mchung@2172: return 0; mchung@2172: } else { mchung@2172: return this.archive.getFileName().compareTo(o.archive.getFileName()); mchung@2172: } mchung@2172: } mchung@2172: return this.target.compareTo(o.target); mchung@2172: } mchung@1577: } mchung@1577: public abstract void visit(Location o, Location t); mchung@1577: } mchung@1577: mchung@1577: private class ClassVisitor extends ArchiveDeps { mchung@1577: ClassVisitor(Archive archive) { mchung@1577: super(archive); mchung@1577: } mchung@2172: @Override mchung@1577: public void visit(Location o, Location t) { mchung@2172: Archive targetArchive = mchung@2172: this.archive.getClasses().contains(t) ? this.archive : map.get(t); mchung@2172: if (targetArchive == null) { mchung@2172: map.put(t, targetArchive = NOT_FOUND); mchung@2172: } mchung@2172: mchung@2172: String origin = o.getClassName(); mchung@2172: String target = t.getClassName(); mchung@2172: add(origin, target, targetArchive, t.getPackageName()); 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@2172: @Override mchung@1577: public void visit(Location o, Location t) { mchung@2172: Archive targetArchive = mchung@2172: this.archive.getClasses().contains(t) ? this.archive : map.get(t); mchung@2172: if (targetArchive == null) { mchung@2172: map.put(t, targetArchive = NOT_FOUND); mchung@2172: } mchung@2172: mchung@2172: String origin = packageOf(o); mchung@2172: String target = packageOf(t); mchung@2172: add(origin, target, targetArchive, t.getPackageName()); mchung@1577: } mchung@2172: public String packageOf(Location o) { mchung@2172: String pkg = o.getPackageName(); mchung@1577: return pkg.isEmpty() ? "" : pkg; mchung@1577: } mchung@1577: } mchung@1577: }