Sat, 07 Nov 2020 10:30:02 +0800
Added tag mips-jdk8u275-b01 for changeset eb6ee6a5f2fe
1 /*
2 * Copyright (c) 2013, 2014, 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;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Objects;
32 import java.util.Set;
33 import java.util.SortedMap;
34 import java.util.SortedSet;
35 import java.util.TreeMap;
36 import java.util.TreeSet;
38 import com.sun.tools.classfile.Dependency.Location;
39 import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
41 /**
42 * Dependency Analyzer.
43 */
44 public class Analyzer {
45 /**
46 * Type of the dependency analysis. Appropriate level of data
47 * will be stored.
48 */
49 public enum Type {
50 SUMMARY,
51 PACKAGE,
52 CLASS,
53 VERBOSE
54 };
56 /**
57 * Filter to be applied when analyzing the dependencies from the given archives.
58 * Only the accepted dependencies are recorded.
59 */
60 interface Filter {
61 boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
62 }
64 private final Type type;
65 private final Filter filter;
66 private final Map<Archive, ArchiveDeps> results = new HashMap<>();
67 private final Map<Location, Archive> map = new HashMap<>();
68 private final Archive NOT_FOUND
69 = new Archive(JdepsTask.getMessage("artifact.not.found"));
71 /**
72 * Constructs an Analyzer instance.
73 *
74 * @param type Type of the dependency analysis
75 * @param filter
76 */
77 public Analyzer(Type type, Filter filter) {
78 this.type = type;
79 this.filter = filter;
80 }
82 /**
83 * Performs the dependency analysis on the given archives.
84 */
85 public void run(List<Archive> archives) {
86 // build a map from Location to Archive
87 buildLocationArchiveMap(archives);
89 // traverse and analyze all dependencies
90 for (Archive archive : archives) {
91 ArchiveDeps deps = new ArchiveDeps(archive, type);
92 archive.visitDependences(deps);
93 results.put(archive, deps);
94 }
95 }
97 private void buildLocationArchiveMap(List<Archive> archives) {
98 // build a map from Location to Archive
99 for (Archive archive: archives) {
100 for (Location l: archive.getClasses()) {
101 if (!map.containsKey(l)) {
102 map.put(l, archive);
103 } else {
104 // duplicated class warning?
105 }
106 }
107 }
108 }
110 public boolean hasDependences(Archive archive) {
111 if (results.containsKey(archive)) {
112 return results.get(archive).dependencies().size() > 0;
113 }
114 return false;
115 }
117 public Set<String> dependences(Archive source) {
118 ArchiveDeps result = results.get(source);
119 return result.targetDependences();
120 }
122 public interface Visitor {
123 /**
124 * Visits a recorded dependency from origin to target which can be
125 * a fully-qualified classname, a package name, a module or
126 * archive name depending on the Analyzer's type.
127 */
128 public void visitDependence(String origin, Archive originArchive,
129 String target, Archive targetArchive);
130 }
132 /**
133 * Visit the dependencies of the given source.
134 * If the requested level is SUMMARY, it will visit the required archives list.
135 */
136 public void visitDependences(Archive source, Visitor v, Type level) {
137 if (level == Type.SUMMARY) {
138 final ArchiveDeps result = results.get(source);
139 SortedMap<String, Archive> sorted = new TreeMap<>();
140 for (Archive a : result.requires()) {
141 sorted.put(a.getName(), a);
142 }
143 for (Archive archive : sorted.values()) {
144 Profile profile = result.getTargetProfile(archive);
145 v.visitDependence(source.getName(), source,
146 profile != null ? profile.profileName() : archive.getName(), archive);
147 }
148 } else {
149 ArchiveDeps result = results.get(source);
150 if (level != type) {
151 // requesting different level of analysis
152 result = new ArchiveDeps(source, level);
153 source.visitDependences(result);
154 }
155 SortedSet<Dep> sorted = new TreeSet<>(result.dependencies());
156 for (Dep d : sorted) {
157 v.visitDependence(d.origin(), d.originArchive(), d.target(), d.targetArchive());
158 }
159 }
160 }
162 public void visitDependences(Archive source, Visitor v) {
163 visitDependences(source, v, type);
164 }
166 /**
167 * ArchiveDeps contains the dependencies for an Archive that can have one or
168 * more classes.
169 */
170 class ArchiveDeps implements Archive.Visitor {
171 protected final Archive archive;
172 protected final Set<Archive> requires;
173 protected final Set<Dep> deps;
174 protected final Type level;
175 private Profile profile;
176 ArchiveDeps(Archive archive, Type level) {
177 this.archive = archive;
178 this.deps = new HashSet<>();
179 this.requires = new HashSet<>();
180 this.level = level;
181 }
183 Set<Dep> dependencies() {
184 return deps;
185 }
187 Set<String> targetDependences() {
188 Set<String> targets = new HashSet<>();
189 for (Dep d : deps) {
190 targets.add(d.target());
191 }
192 return targets;
193 }
195 Set<Archive> requires() {
196 return requires;
197 }
199 Profile getTargetProfile(Archive target) {
200 return JDKArchive.isProfileArchive(target) ? profile : null;
201 }
203 Archive findArchive(Location t) {
204 Archive target = archive.getClasses().contains(t) ? archive : map.get(t);
205 if (target == null) {
206 map.put(t, target = NOT_FOUND);
207 }
208 return target;
209 }
211 // return classname or package name depedning on the level
212 private String getLocationName(Location o) {
213 if (level == Type.CLASS || level == Type.VERBOSE) {
214 return o.getClassName();
215 } else {
216 String pkg = o.getPackageName();
217 return pkg.isEmpty() ? "<unnamed>" : pkg;
218 }
219 }
221 @Override
222 public void visit(Location o, Location t) {
223 Archive targetArchive = findArchive(t);
224 if (filter.accepts(o, archive, t, targetArchive)) {
225 addDep(o, t);
226 if (archive != targetArchive && !requires.contains(targetArchive)) {
227 requires.add(targetArchive);
228 }
229 }
230 if (targetArchive instanceof JDKArchive) {
231 Profile p = Profile.getProfile(t.getPackageName());
232 if (profile == null || (p != null && p.compareTo(profile) > 0)) {
233 profile = p;
234 }
235 }
236 }
238 private Dep curDep;
239 protected Dep addDep(Location o, Location t) {
240 String origin = getLocationName(o);
241 String target = getLocationName(t);
242 Archive targetArchive = findArchive(t);
243 if (curDep != null &&
244 curDep.origin().equals(origin) &&
245 curDep.originArchive() == archive &&
246 curDep.target().equals(target) &&
247 curDep.targetArchive() == targetArchive) {
248 return curDep;
249 }
251 Dep e = new Dep(origin, archive, target, targetArchive);
252 if (deps.contains(e)) {
253 for (Dep e1 : deps) {
254 if (e.equals(e1)) {
255 curDep = e1;
256 }
257 }
258 } else {
259 deps.add(e);
260 curDep = e;
261 }
262 return curDep;
263 }
264 }
266 /*
267 * Class-level or package-level dependency
268 */
269 class Dep implements Comparable<Dep> {
270 final String origin;
271 final Archive originArchive;
272 final String target;
273 final Archive targetArchive;
275 Dep(String origin, Archive originArchive, String target, Archive targetArchive) {
276 this.origin = origin;
277 this.originArchive = originArchive;
278 this.target = target;
279 this.targetArchive = targetArchive;
280 }
282 String origin() {
283 return origin;
284 }
286 Archive originArchive() {
287 return originArchive;
288 }
290 String target() {
291 return target;
292 }
294 Archive targetArchive() {
295 return targetArchive;
296 }
298 @Override
299 @SuppressWarnings("unchecked")
300 public boolean equals(Object o) {
301 if (o instanceof Dep) {
302 Dep d = (Dep) o;
303 return this.origin.equals(d.origin) &&
304 this.originArchive == d.originArchive &&
305 this.target.equals(d.target) &&
306 this.targetArchive == d.targetArchive;
307 }
308 return false;
309 }
311 @Override
312 public int hashCode() {
313 int hash = 7;
314 hash = 67*hash + Objects.hashCode(this.origin)
315 + Objects.hashCode(this.originArchive)
316 + Objects.hashCode(this.target)
317 + Objects.hashCode(this.targetArchive);
318 return hash;
319 }
321 @Override
322 public int compareTo(Dep o) {
323 if (this.origin.equals(o.origin)) {
324 if (this.target.equals(o.target)) {
325 if (this.originArchive == o.originArchive &&
326 this.targetArchive == o.targetArchive) {
327 return 0;
328 } else if (this.originArchive == o.originArchive) {
329 return this.targetArchive.getPathName().compareTo(o.targetArchive.getPathName());
330 } else {
331 return this.originArchive.getPathName().compareTo(o.originArchive.getPathName());
332 }
333 } else {
334 return this.target.compareTo(o.target);
335 }
336 }
337 return this.origin.compareTo(o.origin);
338 }
339 }
340 }