langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java
changeset 16290 b0b4f52de7ea
child 16550 f20e2521f3df
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java	Thu Feb 14 09:43:00 2013 -0800
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tools.jdeps;
+
+import com.sun.tools.classfile.Dependency.Location;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * Dependency Analyzer.
+ */
+public class Analyzer {
+    /**
+     * Type of the dependency analysis.  Appropriate level of data
+     * will be stored.
+     */
+    public enum Type {
+        SUMMARY,
+        PACKAGE,
+        CLASS,
+        VERBOSE
+    };
+
+    private final Type type;
+    private final List<ArchiveDeps> results = new ArrayList<ArchiveDeps>();
+    private final Map<String, Archive> map = new HashMap<String, Archive>();
+    private final Archive NOT_FOUND
+        = new Archive(JdepsTask.getMessage("artifact.not.found"));
+
+    /**
+     * Constructs an Analyzer instance.
+     *
+     * @param type Type of the dependency analysis
+     */
+    public Analyzer(Type type) {
+        this.type = type;
+    }
+
+    /**
+     * Performs the dependency analysis on the given archives.
+     */
+    public void run(List<Archive> archives) {
+        for (Archive archive : archives) {
+            ArchiveDeps deps;
+            if (type == Type.CLASS || type == Type.VERBOSE) {
+                deps = new ClassVisitor(archive);
+            } else {
+                deps = new PackageVisitor(archive);
+            }
+            archive.visit(deps);
+            results.add(deps);
+        }
+
+        // set the required dependencies
+        for (ArchiveDeps result: results) {
+            for (Set<String> set : result.deps.values()) {
+                for (String target : set) {
+                    Archive source = getArchive(target);
+                    if (result.archive != source) {
+                        if (!result.requiredArchives.contains(source)) {
+                            result.requiredArchives.add(source);
+                        }
+                        // either a profile name or the archive name
+                        String tname = getProfile(target);
+                        if (tname.isEmpty()){
+                            tname = source.toString();
+                        }
+                        if (!result.targetNames.contains(tname)) {
+                            result.targetNames.add(tname);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public interface Visitor {
+        /**
+         * Visits a recorded dependency from origin to target which can be
+         * a fully-qualified classname, a package name, a profile or
+         * archive name depending on the Analyzer's type.
+         */
+        void visit(String origin, String target);
+        /**
+         * Visits the source archive to its destination archive of
+         * a recorded dependency.
+         */
+        void visit(Archive source, Archive dest);
+    }
+
+    public void visitSummary(Visitor v) {
+        for (ArchiveDeps r : results) {
+            for (Archive a : r.requiredArchives) {
+                v.visit(r.archive, a);
+            }
+            for (String name : r.targetNames) {
+                v.visit(r.archive.getFileName(), name);
+            }
+        }
+    }
+
+    public void visit(Visitor v) {
+        for (ArchiveDeps r: results) {
+            for (Archive a : r.requiredArchives) {
+                v.visit(r.archive, a);
+            }
+            for (String origin : r.deps.keySet()) {
+                for (String target : r.deps.get(origin)) {
+                    // filter intra-dependency unless in verbose mode
+                    if (type == Type.VERBOSE || getArchive(origin) != getArchive(target)) {
+                        v.visit(origin, target);
+                    }
+                }
+            }
+        }
+    }
+
+    public Archive getArchive(String name) {
+        return map.containsKey(name) ? map.get(name) : NOT_FOUND;
+    }
+
+    public String getArchiveName(String name) {
+        return getArchive(name).getFileName();
+    }
+
+    public String getProfile(String name) {
+        String pn = type == Type.CLASS ? packageOf(name) : name;
+        Archive source = map.get(name);
+        if (source != null && PlatformClassPath.contains(source)) {
+            String profile = PlatformClassPath.getProfileName(pn);
+            if (profile.isEmpty()) {
+                return "JDK internal API (" + source.getFileName() + ")";
+            }
+            return profile;
+        }
+        return "";
+    }
+
+    private abstract class ArchiveDeps implements Archive.Visitor {
+        final Archive archive;
+        final Set<Archive> requiredArchives;
+        final SortedSet<String> targetNames;
+        final SortedMap<String, SortedSet<String>> deps;
+
+        ArchiveDeps(Archive archive) {
+            this.archive = archive;
+            this.requiredArchives = new HashSet<Archive>();
+            this.targetNames = new TreeSet<String>();
+            this.deps = new TreeMap<String, SortedSet<String>>();
+        }
+
+        void add(String loc) {
+            Archive a = map.get(loc);
+            if (a == null) {
+                map.put(loc, archive);
+            } else if (a != archive) {
+                // duplicated class warning?
+            }
+        }
+
+        void add(String origin, String target) {
+            SortedSet<String> set = deps.get(origin);
+            if (set == null) {
+                set = new TreeSet<String>();
+                deps.put(origin, set);
+            }
+            if (!set.contains(target)) {
+                set.add(target);
+            }
+        }
+
+        public abstract void visit(Location o, Location t);
+    }
+
+    private class ClassVisitor extends ArchiveDeps {
+        ClassVisitor(Archive archive) {
+            super(archive);
+        }
+        public void visit(Location l) {
+            add(l.getClassName());
+        }
+        public void visit(Location o, Location t) {
+            add(o.getClassName(), t.getClassName());
+        }
+    }
+
+    private class PackageVisitor extends ArchiveDeps {
+        PackageVisitor(Archive archive) {
+            super(archive);
+        }
+        public void visit(Location o, Location t) {
+            add(packageOf(o), packageOf(t));
+        }
+
+        public void visit(Location l) {
+            add(packageOf(l));
+        }
+
+        private String packageOf(Location loc) {
+            String pkg = loc.getPackageName();
+            return pkg.isEmpty() ? "<unnamed>" : pkg;
+        }
+    }
+
+    private static String packageOf(String cn) {
+        int i = cn.lastIndexOf('.');
+        return (i > 0) ? cn.substring(0, i) : "<unnamed>";
+    }
+}