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@1577: import java.util.ArrayList; mchung@1577: import java.util.HashMap; mchung@1577: import java.util.HashSet; 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@1577: private final List results = new ArrayList(); mchung@1577: 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@1577: results.add(deps); mchung@1577: } mchung@1577: mchung@1577: // set the required dependencies mchung@1577: for (ArchiveDeps result: results) { 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@1577: if (!result.requiredArchives.contains(source)) { mchung@1577: result.requiredArchives.add(source); mchung@1577: } mchung@1577: // either a profile name or the archive name mchung@1638: String tname = result.getTargetProfile(target); mchung@1638: if (tname.isEmpty()) { mchung@1638: tname = PlatformClassPath.contains(source) mchung@1638: ? "JDK internal API (" + source.getFileName() + ")" mchung@1638: : source.toString(); mchung@1577: } mchung@1577: if (!result.targetNames.contains(tname)) { mchung@1577: result.targetNames.add(tname); mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: mchung@1577: public interface Visitor { mchung@1577: /** 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@1638: void visit(String origin, String target, String profile); mchung@1577: /** mchung@1577: * Visits the source archive to its destination archive of mchung@1577: * a recorded dependency. mchung@1577: */ mchung@1577: void visit(Archive source, Archive dest); mchung@1577: } mchung@1577: mchung@1577: public void visitSummary(Visitor v) { mchung@1577: for (ArchiveDeps r : results) { mchung@1577: for (Archive a : r.requiredArchives) { mchung@1577: v.visit(r.archive, a); mchung@1577: } mchung@1577: for (String name : r.targetNames) { mchung@1638: v.visit(r.archive.getFileName(), name, name); mchung@1577: } mchung@1577: } mchung@1577: } mchung@1577: mchung@1577: public void visit(Visitor v) { mchung@1577: for (ArchiveDeps r: results) { mchung@1577: for (Archive a : r.requiredArchives) { mchung@1577: v.visit(r.archive, a); mchung@1577: } mchung@1577: for (String origin : r.deps.keySet()) { mchung@1577: for (String target : r.deps.get(origin)) { mchung@1577: // filter intra-dependency unless in verbose mode mchung@1577: if (type == Type.VERBOSE || getArchive(origin) != getArchive(target)) { mchung@1638: v.visit(origin, target, r.getTargetProfile(target)); mchung@1577: } 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@1638: /** mchung@1638: * Returns the file name of the archive for non-JRE class or mchung@1638: * internal JRE classes. It returns empty string for SE API. mchung@1638: */ mchung@1638: public String getArchiveName(String target, String profile) { mchung@1638: Archive source = getArchive(target); mchung@1638: String name = source.getFileName(); mchung@1638: if (PlatformClassPath.contains(source)) mchung@1638: return profile.isEmpty() ? "JDK internal API (" + name + ")" : ""; mchung@1638: return name; mchung@1577: } mchung@1577: mchung@1577: private abstract class ArchiveDeps implements Archive.Visitor { mchung@1577: final Archive archive; mchung@1577: final Set requiredArchives; mchung@1577: final SortedSet targetNames; mchung@1577: final SortedMap> deps; mchung@1577: mchung@1577: ArchiveDeps(Archive archive) { mchung@1577: this.archive = archive; mchung@1577: this.requiredArchives = new HashSet(); mchung@1577: this.targetNames = new TreeSet(); mchung@1577: 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@1577: set = new TreeSet(); mchung@1577: deps.put(origin, set); mchung@1577: } mchung@1577: if (!set.contains(target)) { mchung@1577: set.add(target); mchung@1577: } mchung@1577: } mchung@1577: mchung@1577: public abstract void visit(Location o, Location t); mchung@1638: public abstract String getTargetProfile(String target); mchung@1638: 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@1638: public String getTargetProfile(String target) { mchung@1638: int i = target.lastIndexOf('.'); mchung@1638: return (i > 0) ? Profiles.getProfileName(target.substring(0, i)) : ""; 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@1638: public String getTargetProfile(String target) { mchung@1638: return Profiles.getProfileName(target); mchung@1638: } mchung@1577: } mchung@1577: }