NewDependencyCollector.java revision 3066:820841f0e8bd
1/*
2 * Copyright (c) 2015, 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
26package com.sun.tools.sjavac.comp.dependencies;
27
28import java.util.Collection;
29import java.util.Collections;
30import java.util.HashMap;
31import java.util.HashSet;
32import java.util.Map;
33import java.util.Set;
34import java.util.stream.Collectors;
35import java.util.stream.Stream;
36
37import javax.tools.JavaFileManager.Location;
38import javax.tools.JavaFileObject;
39import javax.tools.StandardLocation;
40
41import com.sun.source.util.TaskEvent;
42import com.sun.source.util.TaskListener;
43import com.sun.tools.javac.code.Kinds.Kind;
44import com.sun.tools.javac.code.Symbol;
45import com.sun.tools.javac.code.Symbol.ClassSymbol;
46import com.sun.tools.javac.code.Symbol.TypeSymbol;
47import com.sun.tools.javac.code.Type;
48import com.sun.tools.javac.util.Context;
49import com.sun.tools.javac.util.DefinedBy;
50import com.sun.tools.javac.util.DefinedBy.Api;
51import com.sun.tools.javac.util.Dependencies.GraphDependencies;
52import com.sun.tools.javac.util.Dependencies.GraphDependencies.CompletionNode;
53import com.sun.tools.javac.util.GraphUtils.Node;
54import com.sun.tools.sjavac.Util;
55import com.sun.tools.sjavac.comp.JavaFileObjectWithLocation;
56import com.sun.tools.sjavac.comp.PubAPIs;
57
58
59public class NewDependencyCollector implements TaskListener {
60
61    private final Context context;
62    private final Collection<JavaFileObject> explicitJFOs;
63
64    private Map<String, Map<String, Set<String>>> deps;
65    private Map<String, Map<String, Set<String>>> cpDeps;
66
67    public NewDependencyCollector(Context context,
68                                  Collection<JavaFileObject> explicitJFOs) {
69        this.context = context;
70        this.explicitJFOs = explicitJFOs;
71    }
72
73    @Override
74    @DefinedBy(Api.COMPILER_TREE)
75    public void finished(TaskEvent e) {
76        if (e.getKind() == TaskEvent.Kind.COMPILATION) {
77            collectPubApisOfDependencies(context, explicitJFOs);
78            deps = getDependencies(context, explicitJFOs, false);
79            cpDeps = getDependencies(context, explicitJFOs, true);
80        }
81    }
82
83    public Map<String, Map<String, Set<String>>> getDependencies(boolean cp) {
84        return cp ? cpDeps : deps;
85    }
86
87    private Set<CompletionNode> getDependencyNodes(Context context,
88                                                   Collection<JavaFileObject> explicitJFOs,
89                                                   boolean explicits) {
90        GraphDependencies deps = (GraphDependencies) GraphDependencies.instance(context);
91
92        return deps.getNodes()
93                   .stream()
94                   .map(n -> (CompletionNode) n)
95                   .filter(n -> n.getClassSymbol().fullname != null)
96                   .filter(n -> explicits == explicitJFOs.contains(n.getClassSymbol().classfile))
97                   .collect(Collectors.toSet());
98    }
99
100    private void collectPubApisOfDependencies(Context context,
101                                              Collection<JavaFileObject> explicitJFOs) {
102        PubAPIs pubApis = PubAPIs.instance(context);
103        for (CompletionNode cDepNode : getDependencyNodes(context, explicitJFOs, false)) {
104            ClassSymbol cs = cDepNode.getClassSymbol().outermostClass();
105            Location loc = getLocationOf(cs);
106            // We're completely ignorant of PLATFORM_CLASS_PATH classes
107            if (loc == StandardLocation.CLASS_PATH || loc == StandardLocation.SOURCE_PATH)
108                pubApis.visitPubapi(cs);
109        }
110    }
111
112    private Location getLocationOf(ClassSymbol cs) {
113        JavaFileObject jfo = cs.outermostClass().classfile;
114        if (jfo instanceof JavaFileObjectWithLocation) {
115            return ((JavaFileObjectWithLocation<?>) jfo).getLocation();
116        }
117
118        // jfo is most likely on PLATFORM_CLASS_PATH.
119        // See notes in SmartFileManager::locWrap
120
121        return null;
122    }
123
124    // :Package -> fully qualified class name [from] -> set of fully qualified class names [to]
125    private Map<String, Map<String, Set<String>>> getDependencies(Context context,
126                                                                  Collection<JavaFileObject> explicitJFOs,
127                                                                  boolean cp) {
128        Map<String, Map<String, Set<String>>> result = new HashMap<>();
129
130        for (CompletionNode cnode : getDependencyNodes(context, explicitJFOs, true)) {
131
132            String fqDep = cnode.getClassSymbol().outermostClass().flatname.toString();
133            String depPkg = Util.pkgNameOfClassName(fqDep);
134
135            Map<String, Set<String>> depsForThisClass = result.get(depPkg);
136            if (depsForThisClass == null) {
137                result.put(depPkg, depsForThisClass = new HashMap<>());
138            }
139
140            Set<String> fqDeps = depsForThisClass.get(fqDep);
141            if (fqDeps == null) {
142                depsForThisClass.put(fqDep, fqDeps = new HashSet<>());
143            }
144
145            for (Node<?,?> depNode : getAllDependencies(cnode)) {
146                CompletionNode cDepNode = (CompletionNode) depNode;
147                // Symbol is not regarded to depend on itself.
148                if (cDepNode == cnode) {
149                    continue;
150                }
151                // Skip anonymous classes
152                if (cDepNode.getClassSymbol().fullname == null) {
153                    continue;
154                }
155                if (isSymbolRelevant(cp, cDepNode.getClassSymbol())) {
156                    fqDeps.add(cDepNode.getClassSymbol().outermostClass().flatname.toString());
157                }
158            }
159
160            // The completion dependency graph is not transitively closed for inheritance relations.
161            // For sjavac's purposes however, a class depends on it's super super type, so below we
162            // make sure that we include super types.
163            for (ClassSymbol cs : allSupertypes(cnode.getClassSymbol())) {
164                if (isSymbolRelevant(cp, cs)) {
165                    fqDeps.add(cs.outermostClass().flatname.toString());
166                }
167            }
168
169        }
170        return result;
171    }
172
173    public boolean isSymbolRelevant(boolean cp, ClassSymbol cs) {
174        Location csLoc = getLocationOf(cs);
175        Location relevantLocation = cp ? StandardLocation.CLASS_PATH : StandardLocation.SOURCE_PATH;
176        return csLoc == relevantLocation;
177    }
178
179    private Set<ClassSymbol> allSupertypes(TypeSymbol t) {
180        if (t == null || !(t instanceof ClassSymbol)) {
181            return Collections.emptySet();
182        }
183        Set<ClassSymbol> result = new HashSet<>();
184        ClassSymbol cs = (ClassSymbol) t;
185        result.add(cs);
186        result.addAll(allSupertypes(cs.getSuperclass().tsym));
187        for (Type it : cs.getInterfaces()) {
188            result.addAll(allSupertypes(it.tsym));
189        }
190        return result;
191    }
192
193    private Collection<? extends Node<?, ?>> getAllDependencies(CompletionNode cnode) {
194        return Stream.of(cnode.getSupportedDependencyKinds())
195                     .flatMap(dk -> cnode.getDependenciesByKind(dk).stream())
196                     .collect(Collectors.toSet());
197    }
198}
199