1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 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 package com.sun.tools.jdeps; 26 27 import com.sun.tools.classfile.Dependency.Location; 28 import java.util.ArrayList; 29 import java.util.HashMap; 30 import java.util.HashSet; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Set; 34 import java.util.SortedMap; 35 import java.util.SortedSet; 36 import java.util.TreeMap; 37 import java.util.TreeSet; 38 39 /** 40 * Dependency Analyzer. 41 */ 42 public class Analyzer { 43 /** 44 * Type of the dependency analysis. Appropriate level of data 45 * will be stored. 46 */ 47 public enum Type { 48 SUMMARY, 49 PACKAGE, 50 CLASS, 51 VERBOSE 52 }; 53 54 private final Type type; 55 private final List<ArchiveDeps> results = new ArrayList<ArchiveDeps>(); 56 private final Map<String, Archive> map = new HashMap<String, Archive>(); 57 private final Archive NOT_FOUND 58 = new Archive(JdepsTask.getMessage("artifact.not.found")); 59 60 /** 61 * Constructs an Analyzer instance. 62 * 63 * @param type Type of the dependency analysis 64 */ 65 public Analyzer(Type type) { 66 this.type = type; 67 } 68 69 /** 70 * Performs the dependency analysis on the given archives. 71 */ 72 public void run(List<Archive> archives) { 73 for (Archive archive : archives) { 74 ArchiveDeps deps; 75 if (type == Type.CLASS || type == Type.VERBOSE) { 76 deps = new ClassVisitor(archive); 77 } else { 78 deps = new PackageVisitor(archive); 79 } 80 archive.visit(deps); 81 results.add(deps); 82 } 83 84 // set the required dependencies 85 for (ArchiveDeps result: results) { 86 for (Set<String> set : result.deps.values()) { 87 for (String target : set) { 88 Archive source = getArchive(target); 89 if (result.archive != source) { 90 if (!result.requiredArchives.contains(source)) { 91 result.requiredArchives.add(source); 92 } 93 // either a profile name or the archive name 94 String tname = result.getTargetProfile(target); 95 if (tname.isEmpty()) { 96 tname = PlatformClassPath.contains(source) 97 ? "JDK internal API (" + source.getFileName() + ")" 98 : source.toString(); 99 } 100 if (!result.targetNames.contains(tname)) { 101 result.targetNames.add(tname); 102 } 103 } 104 } 105 } 106 } 107 } 108 109 public interface Visitor { 110 /** 111 * Visits a recorded dependency from origin to target which can be 112 * a fully-qualified classname, a package name, a profile or 113 * archive name depending on the Analyzer's type. 114 */ 115 void visit(String origin, String target, String profile); 116 /** 117 * Visits the source archive to its destination archive of 118 * a recorded dependency. 119 */ 120 void visit(Archive source, Archive dest); 121 } 122 123 public void visitSummary(Visitor v) { 124 for (ArchiveDeps r : results) { 125 for (Archive a : r.requiredArchives) { 126 v.visit(r.archive, a); 127 } 128 for (String name : r.targetNames) { 129 v.visit(r.archive.getFileName(), name, name); 130 } 131 } 132 } 133 134 public void visit(Visitor v) { 135 for (ArchiveDeps r: results) { 136 for (Archive a : r.requiredArchives) { 137 v.visit(r.archive, a); 138 } 139 for (String origin : r.deps.keySet()) { 140 for (String target : r.deps.get(origin)) { 141 // filter intra-dependency unless in verbose mode 142 if (type == Type.VERBOSE || getArchive(origin) != getArchive(target)) { 143 v.visit(origin, target, r.getTargetProfile(target)); 144 } 145 } 146 } 147 } 148 } 149 150 public Archive getArchive(String name) { 151 return map.containsKey(name) ? map.get(name) : NOT_FOUND; 152 } 153 154 /** 155 * Returns the file name of the archive for non-JRE class or 156 * internal JRE classes. It returns empty string for SE API. 157 */ 158 public String getArchiveName(String target, String profile) { 159 Archive source = getArchive(target); 160 String name = source.getFileName(); 161 if (PlatformClassPath.contains(source)) 162 return profile.isEmpty() ? "JDK internal API (" + name + ")" : ""; 163 return name; 164 } 165 166 private abstract class ArchiveDeps implements Archive.Visitor { 167 final Archive archive; 168 final Set<Archive> requiredArchives; 169 final SortedSet<String> targetNames; 170 final SortedMap<String, SortedSet<String>> deps; 171 172 ArchiveDeps(Archive archive) { 173 this.archive = archive; 174 this.requiredArchives = new HashSet<Archive>(); 175 this.targetNames = new TreeSet<String>(); 176 this.deps = new TreeMap<String, SortedSet<String>>(); 177 } 178 179 void add(String loc) { 180 Archive a = map.get(loc); 181 if (a == null) { 182 map.put(loc, archive); 183 } else if (a != archive) { 184 // duplicated class warning? 185 } 186 } 187 188 void add(String origin, String target) { 189 SortedSet<String> set = deps.get(origin); 190 if (set == null) { 191 set = new TreeSet<String>(); 192 deps.put(origin, set); 193 } 194 if (!set.contains(target)) { 195 set.add(target); 196 } 197 } 198 199 public abstract void visit(Location o, Location t); 200 public abstract String getTargetProfile(String target); 201 202 } 203 204 private class ClassVisitor extends ArchiveDeps { 205 ClassVisitor(Archive archive) { 206 super(archive); 207 } 208 public void visit(Location l) { 209 add(l.getClassName()); 210 } 211 public void visit(Location o, Location t) { 212 add(o.getClassName(), t.getClassName()); 213 } 214 public String getTargetProfile(String target) { 215 int i = target.lastIndexOf('.'); 216 return (i > 0) ? Profiles.getProfileName(target.substring(0, i)) : ""; 217 } 218 } 219 220 private class PackageVisitor extends ArchiveDeps { 221 PackageVisitor(Archive archive) { 222 super(archive); 223 } 224 public void visit(Location o, Location t) { 225 add(packageOf(o), packageOf(t)); 226 } 227 public void visit(Location l) { 228 add(packageOf(l)); 229 } 230 private String packageOf(Location loc) { 231 String pkg = loc.getPackageName(); 232 return pkg.isEmpty() ? "<unnamed>" : pkg; 233 } 234 public String getTargetProfile(String target) { 235 return Profiles.getProfileName(target); 236 } 237 } 238 }