src/share/classes/com/sun/tools/jdeps/Analyzer.java

Mon, 16 Oct 2017 16:07:48 +0800

author
aoqi
date
Mon, 16 Oct 2017 16:07:48 +0800
changeset 2893
ca5783d9a597
parent 2802
6b43535fb9f8
parent 2702
9ca8d8713094
permissions
-rw-r--r--

merge

aoqi@0 1 /*
mchung@2538 2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25 package com.sun.tools.jdeps;
aoqi@0 26
aoqi@0 27 import java.util.HashMap;
mchung@2538 28 import java.util.HashSet;
aoqi@0 29 import java.util.List;
aoqi@0 30 import java.util.Map;
aoqi@0 31 import java.util.Objects;
aoqi@0 32 import java.util.Set;
aoqi@0 33 import java.util.SortedMap;
aoqi@0 34 import java.util.SortedSet;
aoqi@0 35 import java.util.TreeMap;
aoqi@0 36 import java.util.TreeSet;
aoqi@0 37
mchung@2538 38 import com.sun.tools.classfile.Dependency.Location;
mchung@2538 39 import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
mchung@2538 40
aoqi@0 41 /**
aoqi@0 42 * Dependency Analyzer.
aoqi@0 43 */
aoqi@0 44 public class Analyzer {
aoqi@0 45 /**
aoqi@0 46 * Type of the dependency analysis. Appropriate level of data
aoqi@0 47 * will be stored.
aoqi@0 48 */
aoqi@0 49 public enum Type {
aoqi@0 50 SUMMARY,
aoqi@0 51 PACKAGE,
aoqi@0 52 CLASS,
aoqi@0 53 VERBOSE
aoqi@0 54 };
aoqi@0 55
mchung@2538 56 /**
mchung@2538 57 * Filter to be applied when analyzing the dependencies from the given archives.
mchung@2538 58 * Only the accepted dependencies are recorded.
mchung@2538 59 */
mchung@2538 60 interface Filter {
mchung@2538 61 boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
mchung@2538 62 }
mchung@2538 63
aoqi@0 64 private final Type type;
mchung@2538 65 private final Filter filter;
aoqi@0 66 private final Map<Archive, ArchiveDeps> results = new HashMap<>();
aoqi@0 67 private final Map<Location, Archive> map = new HashMap<>();
aoqi@0 68 private final Archive NOT_FOUND
aoqi@0 69 = new Archive(JdepsTask.getMessage("artifact.not.found"));
aoqi@0 70
aoqi@0 71 /**
aoqi@0 72 * Constructs an Analyzer instance.
aoqi@0 73 *
aoqi@0 74 * @param type Type of the dependency analysis
mchung@2538 75 * @param filter
aoqi@0 76 */
mchung@2538 77 public Analyzer(Type type, Filter filter) {
aoqi@0 78 this.type = type;
mchung@2538 79 this.filter = filter;
aoqi@0 80 }
aoqi@0 81
aoqi@0 82 /**
aoqi@0 83 * Performs the dependency analysis on the given archives.
aoqi@0 84 */
aoqi@0 85 public void run(List<Archive> archives) {
aoqi@0 86 // build a map from Location to Archive
mchung@2538 87 buildLocationArchiveMap(archives);
mchung@2538 88
mchung@2538 89 // traverse and analyze all dependencies
mchung@2538 90 for (Archive archive : archives) {
mchung@2538 91 ArchiveDeps deps = new ArchiveDeps(archive, type);
mchung@2538 92 archive.visitDependences(deps);
mchung@2538 93 results.put(archive, deps);
mchung@2538 94 }
mchung@2538 95 }
mchung@2538 96
mchung@2538 97 private void buildLocationArchiveMap(List<Archive> archives) {
mchung@2538 98 // build a map from Location to Archive
aoqi@0 99 for (Archive archive: archives) {
aoqi@0 100 for (Location l: archive.getClasses()) {
aoqi@0 101 if (!map.containsKey(l)) {
aoqi@0 102 map.put(l, archive);
aoqi@0 103 } else {
aoqi@0 104 // duplicated class warning?
aoqi@0 105 }
aoqi@0 106 }
aoqi@0 107 }
aoqi@0 108 }
aoqi@0 109
aoqi@0 110 public boolean hasDependences(Archive archive) {
aoqi@0 111 if (results.containsKey(archive)) {
mchung@2538 112 return results.get(archive).dependencies().size() > 0;
aoqi@0 113 }
aoqi@0 114 return false;
aoqi@0 115 }
aoqi@0 116
mchung@2539 117 public Set<String> dependences(Archive source) {
mchung@2539 118 ArchiveDeps result = results.get(source);
mchung@2539 119 return result.targetDependences();
mchung@2539 120 }
mchung@2539 121
aoqi@0 122 public interface Visitor {
aoqi@0 123 /**
aoqi@0 124 * Visits a recorded dependency from origin to target which can be
mchung@2538 125 * a fully-qualified classname, a package name, a module or
aoqi@0 126 * archive name depending on the Analyzer's type.
aoqi@0 127 */
mchung@2538 128 public void visitDependence(String origin, Archive originArchive,
mchung@2538 129 String target, Archive targetArchive);
aoqi@0 130 }
aoqi@0 131
mchung@2538 132 /**
mchung@2538 133 * Visit the dependencies of the given source.
mchung@2538 134 * If the requested level is SUMMARY, it will visit the required archives list.
mchung@2538 135 */
mchung@2538 136 public void visitDependences(Archive source, Visitor v, Type level) {
mchung@2538 137 if (level == Type.SUMMARY) {
mchung@2538 138 final ArchiveDeps result = results.get(source);
mchung@2538 139 SortedMap<String, Archive> sorted = new TreeMap<>();
mchung@2538 140 for (Archive a : result.requires()) {
mchung@2538 141 sorted.put(a.getName(), a);
mchung@2538 142 }
mchung@2538 143 for (Archive archive : sorted.values()) {
mchung@2538 144 Profile profile = result.getTargetProfile(archive);
mchung@2538 145 v.visitDependence(source.getName(), source,
mchung@2538 146 profile != null ? profile.profileName() : archive.getName(), archive);
mchung@2538 147 }
mchung@2538 148 } else {
mchung@2538 149 ArchiveDeps result = results.get(source);
mchung@2538 150 if (level != type) {
mchung@2538 151 // requesting different level of analysis
mchung@2538 152 result = new ArchiveDeps(source, level);
mchung@2538 153 source.visitDependences(result);
mchung@2538 154 }
mchung@2538 155 SortedSet<Dep> sorted = new TreeSet<>(result.dependencies());
mchung@2538 156 for (Dep d : sorted) {
mchung@2538 157 v.visitDependence(d.origin(), d.originArchive(), d.target(), d.targetArchive());
mchung@2538 158 }
aoqi@0 159 }
aoqi@0 160 }
aoqi@0 161
aoqi@0 162 public void visitDependences(Archive source, Visitor v) {
mchung@2538 163 visitDependences(source, v, type);
mchung@2538 164 }
mchung@2538 165
mchung@2538 166 /**
mchung@2538 167 * ArchiveDeps contains the dependencies for an Archive that can have one or
mchung@2538 168 * more classes.
mchung@2538 169 */
mchung@2538 170 class ArchiveDeps implements Archive.Visitor {
mchung@2538 171 protected final Archive archive;
mchung@2538 172 protected final Set<Archive> requires;
mchung@2538 173 protected final Set<Dep> deps;
mchung@2538 174 protected final Type level;
mchung@2538 175 private Profile profile;
mchung@2538 176 ArchiveDeps(Archive archive, Type level) {
mchung@2538 177 this.archive = archive;
mchung@2538 178 this.deps = new HashSet<>();
mchung@2538 179 this.requires = new HashSet<>();
mchung@2538 180 this.level = level;
mchung@2538 181 }
mchung@2538 182
mchung@2538 183 Set<Dep> dependencies() {
mchung@2538 184 return deps;
mchung@2538 185 }
mchung@2538 186
mchung@2539 187 Set<String> targetDependences() {
mchung@2539 188 Set<String> targets = new HashSet<>();
mchung@2539 189 for (Dep d : deps) {
mchung@2539 190 targets.add(d.target());
mchung@2539 191 }
mchung@2539 192 return targets;
mchung@2539 193 }
mchung@2539 194
mchung@2538 195 Set<Archive> requires() {
mchung@2538 196 return requires;
mchung@2538 197 }
mchung@2538 198
mchung@2538 199 Profile getTargetProfile(Archive target) {
mchung@2538 200 return JDKArchive.isProfileArchive(target) ? profile : null;
mchung@2538 201 }
mchung@2538 202
mchung@2538 203 Archive findArchive(Location t) {
mchung@2538 204 Archive target = archive.getClasses().contains(t) ? archive : map.get(t);
mchung@2538 205 if (target == null) {
mchung@2538 206 map.put(t, target = NOT_FOUND);
mchung@2538 207 }
mchung@2538 208 return target;
mchung@2538 209 }
mchung@2538 210
mchung@2538 211 // return classname or package name depedning on the level
mchung@2538 212 private String getLocationName(Location o) {
mchung@2538 213 if (level == Type.CLASS || level == Type.VERBOSE) {
mchung@2538 214 return o.getClassName();
mchung@2538 215 } else {
mchung@2538 216 String pkg = o.getPackageName();
mchung@2538 217 return pkg.isEmpty() ? "<unnamed>" : pkg;
mchung@2538 218 }
mchung@2538 219 }
mchung@2538 220
mchung@2538 221 @Override
mchung@2538 222 public void visit(Location o, Location t) {
mchung@2538 223 Archive targetArchive = findArchive(t);
mchung@2538 224 if (filter.accepts(o, archive, t, targetArchive)) {
mchung@2538 225 addDep(o, t);
mchung@2802 226 if (archive != targetArchive && !requires.contains(targetArchive)) {
mchung@2538 227 requires.add(targetArchive);
mchung@2538 228 }
mchung@2538 229 }
mchung@2538 230 if (targetArchive instanceof JDKArchive) {
mchung@2538 231 Profile p = Profile.getProfile(t.getPackageName());
mchung@2538 232 if (profile == null || (p != null && p.compareTo(profile) > 0)) {
mchung@2538 233 profile = p;
aoqi@0 234 }
aoqi@0 235 }
aoqi@0 236 }
aoqi@0 237
mchung@2538 238 private Dep curDep;
mchung@2538 239 protected Dep addDep(Location o, Location t) {
mchung@2538 240 String origin = getLocationName(o);
mchung@2538 241 String target = getLocationName(t);
mchung@2538 242 Archive targetArchive = findArchive(t);
mchung@2538 243 if (curDep != null &&
mchung@2538 244 curDep.origin().equals(origin) &&
mchung@2538 245 curDep.originArchive() == archive &&
mchung@2538 246 curDep.target().equals(target) &&
mchung@2538 247 curDep.targetArchive() == targetArchive) {
mchung@2538 248 return curDep;
mchung@2538 249 }
aoqi@0 250
mchung@2538 251 Dep e = new Dep(origin, archive, target, targetArchive);
mchung@2538 252 if (deps.contains(e)) {
mchung@2538 253 for (Dep e1 : deps) {
mchung@2538 254 if (e.equals(e1)) {
mchung@2538 255 curDep = e1;
aoqi@0 256 }
aoqi@0 257 }
mchung@2538 258 } else {
mchung@2538 259 deps.add(e);
mchung@2538 260 curDep = e;
aoqi@0 261 }
mchung@2538 262 return curDep;
aoqi@0 263 }
aoqi@0 264 }
aoqi@0 265
mchung@2538 266 /*
mchung@2538 267 * Class-level or package-level dependency
mchung@2538 268 */
mchung@2538 269 class Dep implements Comparable<Dep> {
mchung@2538 270 final String origin;
mchung@2538 271 final Archive originArchive;
mchung@2538 272 final String target;
mchung@2538 273 final Archive targetArchive;
mchung@2538 274
mchung@2538 275 Dep(String origin, Archive originArchive, String target, Archive targetArchive) {
mchung@2538 276 this.origin = origin;
mchung@2538 277 this.originArchive = originArchive;
mchung@2538 278 this.target = target;
mchung@2538 279 this.targetArchive = targetArchive;
aoqi@0 280 }
mchung@2538 281
mchung@2538 282 String origin() {
mchung@2538 283 return origin;
mchung@2538 284 }
mchung@2538 285
mchung@2538 286 Archive originArchive() {
mchung@2538 287 return originArchive;
mchung@2538 288 }
mchung@2538 289
mchung@2538 290 String target() {
mchung@2538 291 return target;
mchung@2538 292 }
mchung@2538 293
mchung@2538 294 Archive targetArchive() {
mchung@2538 295 return targetArchive;
mchung@2538 296 }
mchung@2538 297
aoqi@0 298 @Override
mchung@2538 299 @SuppressWarnings("unchecked")
mchung@2538 300 public boolean equals(Object o) {
mchung@2538 301 if (o instanceof Dep) {
mchung@2538 302 Dep d = (Dep) o;
mchung@2538 303 return this.origin.equals(d.origin) &&
mchung@2538 304 this.originArchive == d.originArchive &&
mchung@2538 305 this.target.equals(d.target) &&
mchung@2538 306 this.targetArchive == d.targetArchive;
aoqi@0 307 }
mchung@2538 308 return false;
mchung@2538 309 }
aoqi@0 310
mchung@2538 311 @Override
mchung@2538 312 public int hashCode() {
mchung@2538 313 int hash = 7;
mchung@2538 314 hash = 67*hash + Objects.hashCode(this.origin)
mchung@2538 315 + Objects.hashCode(this.originArchive)
mchung@2538 316 + Objects.hashCode(this.target)
mchung@2538 317 + Objects.hashCode(this.targetArchive);
mchung@2538 318 return hash;
aoqi@0 319 }
mchung@2538 320
mchung@2538 321 @Override
mchung@2538 322 public int compareTo(Dep o) {
mchung@2538 323 if (this.origin.equals(o.origin)) {
mchung@2538 324 if (this.target.equals(o.target)) {
mchung@2538 325 if (this.originArchive == o.originArchive &&
mchung@2538 326 this.targetArchive == o.targetArchive) {
mchung@2538 327 return 0;
mchung@2538 328 } else if (this.originArchive == o.originArchive) {
mchung@2538 329 return this.targetArchive.getPathName().compareTo(o.targetArchive.getPathName());
mchung@2538 330 } else {
mchung@2538 331 return this.originArchive.getPathName().compareTo(o.originArchive.getPathName());
mchung@2538 332 }
mchung@2538 333 } else {
mchung@2538 334 return this.target.compareTo(o.target);
mchung@2538 335 }
mchung@2538 336 }
mchung@2538 337 return this.origin.compareTo(o.origin);
aoqi@0 338 }
aoqi@0 339 }
aoqi@0 340 }

mercurial